Library Environment

suppressMessages(library(tidyverse))
suppressMessages(library(stringr))
suppressMessages(library(ISLR))
suppressMessages(library(caret))
suppressMessages(library(doMC))
suppressMessages(library(plotly))
suppressMessages(library(stringr))
registerDoMC(cores=4)

Load and processing data ctu13 cleaned

myData_cleaned <- read.csv('/home/jguerra/datasets/ctu13.labeled.cleaned', stringsAsFactors = F, sep = '|')
myData_cleaned.bkp = myData_cleaned
myData_cleaned
#Periodicity
myData_cleaned = myData_cleaned %>% mutate(strong_p = str_count(State,'[a-i]'))
myData_cleaned = myData_cleaned %>% mutate(weak_p = str_count(State,'[A-I]'))
myData_cleaned = myData_cleaned %>% mutate(weak_np = str_count(State,'[r-z]'))
myData_cleaned = myData_cleaned %>% mutate(strong_np = str_count(State,'[R-Z]'))
#Duration
myData_cleaned = myData_cleaned %>% mutate(duration_s = str_count(State,'(a|A|r|R|1|d|D|u|U|4|g|G|x|X|7)'))
myData_cleaned = myData_cleaned %>% mutate(duration_m = str_count(State,'(b|B|s|S|2|e|E|v|V|5|h|H|y|Y|8)'))
myData_cleaned = myData_cleaned %>% mutate(duration_l = str_count(State,'(c|C|t|T|3|f|F|w|W|6|i|I|z|Z|9)'))
#Size
myData_cleaned = myData_cleaned %>% mutate(size_s = str_count(State,'[a-c]') + str_count(State,'[A-C]') + str_count(State,'[r-t]') + str_count(State,'[R-T]') + str_count(State,'[1-3]'))
myData_cleaned = myData_cleaned %>% mutate(size_m = str_count(State,'[d-f]') + str_count(State,'[D-F]') + str_count(State,'[u-w]') + str_count(State,'[U-W]') + str_count(State,'[4-6]'))
myData_cleaned = myData_cleaned %>% mutate(size_l = str_count(State,'[g-i]') + str_count(State,'[G-I]') + str_count(State,'[x-z]') + str_count(State,'[X-Z]') + str_count(State,'[7-9]'))
#Periodicity %
myData_cleaned <- myData_cleaned %>% mutate(strong_p = (strong_p / modelsize))
myData_cleaned <- myData_cleaned %>% mutate(weak_p = (weak_p / modelsize))
myData_cleaned <- myData_cleaned %>% mutate(strong_np = (strong_np / modelsize))
myData_cleaned <- myData_cleaned %>% mutate(weak_np = (weak_np / modelsize))
#Duration %
myData_cleaned <- myData_cleaned %>% mutate(duration_s = (duration_s / modelsize))
myData_cleaned <- myData_cleaned %>% mutate(duration_m = (duration_m / modelsize))
myData_cleaned <- myData_cleaned %>% mutate(duration_l = (duration_l / modelsize))
#Size %
myData_cleaned <- myData_cleaned %>% mutate(size_s = (size_s / modelsize))
myData_cleaned <- myData_cleaned %>% mutate(size_m = (size_m / modelsize))
myData_cleaned <- myData_cleaned %>% mutate(size_l = (size_l / modelsize))
#Making feature vectors
feature_vectors_cleaned = myData_cleaned[,c('strong_p','weak_p','weak_np','strong_np','duration_s','duration_m','duration_l','size_s','size_m','size_l','modelsize','label','class','port','proto')]
names(feature_vectors_cleaned) = c("sp","wp","wnp","snp","ds","dm","dl","ss","sm","sl","modelsize","class","subclass","port","proto")
feature_vectors_cleaned$class = factor(feature_vectors_cleaned$class)
feature_vectors_cleaned$subclass = factor(feature_vectors_cleaned$subclass)
feature_vectors_cleaned$proto = factor(feature_vectors_cleaned$proto)
feature_vectors_cleaned

Removing excesive Botnet and Normal class(Making the dataset more equitable)

feature_vectors_cleaned.bkp <- feature_vectors_cleaned
feature_vectors_cleaned %>% group_by(class) %>% summarise(n=n()) %>% arrange(desc(n))
feature_vectors_cleaned_aux_botnet <- feature_vectors_cleaned %>% filter(class == 'Botnet-TCP-SMTP-Attempt-SPAM')
feature_vectors_cleaned_aux_normal <- feature_vectors_cleaned %>% filter(class == 'Normal-TCP-HTTP')
feature_vectors_cleaned_aux_botnet
feature_vectors_cleaned_aux_normal
feature_vectors_cleaned_aux_rest <- feature_vectors_cleaned %>% filter(class != 'Botnet-TCP-SMTP-Attempt-SPAM') %>% filter(class != 'Normal-TCP-HTTP')
feature_vectors_cleaned_aux_rest %>% group_by(class) %>% summarise(n=n()) %>% arrange(desc(n))
aux1 <- rbind(feature_vectors_cleaned_aux_botnet[1:500,],feature_vectors_cleaned_aux_normal[1:500,])
aux1 %>% group_by(class) %>% summarise(n=n()) %>% arrange(desc(n))
aux <- rbind(feature_vectors_cleaned_aux_rest,aux1)
aux %>% group_by(class) %>% summarise(n=n()) %>% arrange(desc(n))
feature_vectors_cleaned <- aux

Create training set and testset

set.seed(212)
trainIndex <- createDataPartition(feature_vectors_cleaned$subclass, p=0.70, list=FALSE)
data_training <- feature_vectors_cleaned[ trainIndex,]
data_testing <- feature_vectors_cleaned[-trainIndex,]
#data_train = data_train %>% filter(length>5)
train <- upSample(x = data_training,  y = data_training$subclass, yname="class")
training <- train[,-c(11,16)]
testing <- data_testing[,-c(11)]
training
testing
nrow(training)
[1] 3322
nrow(feature_vectors_cleaned)
[1] 3089

Training configuration

ctrl_fast <- trainControl(method="cv", 
                     repeats=2,
                     number=10, 
                     summaryFunction=twoClassSummary,
                     verboseIter=T,
                     classProbs=TRUE,
                     allowParallel = TRUE)  

Experiment 1

Creation of cluster and k parameters analysis

library(factoextra)
library(cluster)
library(NbClust)
feature_vector_training = training[,-c(11,12,13,14)]
# K-means clustering
set.seed(321)
#km.res <- kmeans(feature_vector_training, 3, nstart = 25)
km.res <- kmeans(feature_vector_training, 7, nstart = 25)
# k-means group number of each observation
km.res$cluster
   [1] 2 1 2 1 1 5 5 4 2 5 5 2 3 3 3 2 1 2 3 2 2 1 5 3 2 1 3 3 3 3 3 6 3 4 2 2 1 5 2 1 7 3 2 1 1 2 1 1 1 1 3 1 3 1 1 1 3 2 2 2 2 2 2 6 2 2 2 2 6 6 5 6 4 4 2 2
  [77] 2 4 4 2 2 2 2 3 6 4 4 2 2 4 4 6 6 2 2 3 4 2 2 2 4 2 2 6 4 3 2 5 2 2 6 2 2 2 2 4 4 2 2 2 5 7 3 6 3 5 6 2 2 3 4 2 6 5 2 4 4 3 2 6 4 4 4 4 3 5 2 4 4 4 4 4
 [153] 4 6 6 4 6 3 4 5 6 4 6 3 4 4 6 4 4 4 6 2 4 4 3 3 4 6 6 5 4 6 3 4 5 6 5 4 3 4 4 4 4 4 6 6 6 6 6 6 3 3 5 4 4 1 2 2 4 2 4 6 5 4 4 2 7 6 6 6 3 6 6 7 3 4 5 6
 [229] 7 4 5 2 6 5 3 1 1 1 6 4 6 4 6 6 5 6 6 6 6 6 6 6 7 5 7 5 1 3 2 1 2 1 2 1 1 1 1 2 2 3 2 5 5 5 1 7 7 7 7 1 3 1 1 2 3 2 2 3 3 1 1 1 1 2 2 1 3 1 1 2 2 3 4 3
 [305] 1 1 1 2 2 3 1 2 1 3 1 2 1 2 1 2 3 2 2 2 2 2 1 1 2 5 5 1 1 5 1 1 1 1 1 1 1 1 1 1 1 6 1 1 6 2 1 1 1 5 2 1 2 5 2 2 1 4 1 6 2 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [381] 1 1 1 1 1 1 1 1 1 3 2 2 1 1 3 2 1 2 2 2 2 5 1 3 5 3 2 1 2 1 2 2 1 1 1 1 3 1 2 1 5 2 2 1 1 1 3 3 1 1 2 2 2 1 3 2 2 1 1 3 1 2 6 2 6 5 5 5 5 2 2 2 2 2 2 3
 [457] 2 2 2 2 2 2 2 6 1 6 6 6 3 3 6 6 3 6 6 2 1 1 2 1 2 6 4 1 3 2 2 2 3 1 3 2 3 1 1 1 1 1 1 2 2 1 2 1 2 3 1 1 6 2 3 1 1 1 7 1 7 2 3 1 3 3 3 2 2 3 2 1 1 1 1 3
 [533] 1 2 1 2 5 7 4 3 3 3 1 1 2 2 1 2 3 3 2 2 2 1 1 3 1 5 1 3 2 1 1 1 5 1 2 2 3 2 2 2 3 3 3 3 1 2 1 2 2 2 3 3 2 3 1 3 1 1 1 2 1 1 1 1 3 3 3 2 2 1 2 2 2 2 1 1
 [609] 1 6 2 1 1 2 1 1 1 3 1 1 2 2 3 2 1 1 1 1 3 3 1 1 4 2 2 2 1 2 3 2 2 1 5 2 2 1 2 2 1 1 1 2 3 2 2 3 3 3 3 1 1 1 1 1 2 2 2 3 1 1 1 2 2 3 2 1 1 1 3 1 1 4 1 2
 [685] 3 3 2 1 3 3 1 1 1 2 3 5 1 1 1 5 2 2 2 3 1 3 3 1 2 1 1 2 2 2 3 2 1 3 3 1 7 2 3 1 3 2 7 1 2 1 1 2 1 1 1 2 2 3 1 3 1 1 2 1 1 1 3 1 3 2 1 1 1 3 2 1 3 2 4 1
 [761] 1 3 1 2 3 2 1 1 1 1 1 2 1 4 3 3 2 1 1 2 3 2 3 1 1 2 3 2 2 2 2 3 7 2 3 1 3 6 3 1 2 2 2 3 1 1 1 3 1 1 1 1 1 1 1 2 3 2 1 4 1 1 1 3 1 2 1 4 3 3 1 6 2 3 1 1
 [837] 2 1 2 1 1 1 2 1 1 1 1 1 4 1 2 1 7 1 1 1 1 1 2 2 1 1 1 2 1 1 2 2 3 1 1 2 1 2 2 1 1 2 3 3 3 2 3 3 1 1 1 1 2 1 1 2 2 2 1 1 3 1 1 1 1 2 1 3 3 2 2 2 2 1 2 1
 [913] 3 1 5 2 1 1 1 1 2 2 4 2 1 1 3 2 2 2 1 3 2 1 1 1 3 3 2 3 3 1 1 1 2 1 1 1 1 3 3 1 3 3 2 1 1 1 1 1 2 2 1 2 1 3 2 1 2 2 3 2 2 2 5 3 2 2 2 5 1 1 1 1 1 2 2 1
 [989] 1 1 1 3 1 1 1 3 1 2 1 3
 [ reached getOption("max.print") -- omitted 2322 entries ]
# Visualize k-means clusters
fviz_cluster(km.res, data = feature_vector_training, geom = "point",
             stand = FALSE, ellipse.type = "norm")

Elbow analysis

set.seed(321)
# Compute and plot wss for k = 2 to k = 15
k.max <- 15 # Maximal number of clusters
data <- feature_vector_training
wss <- sapply(1:k.max, 
        function(k){kmeans(data, k, nstart=10 )$tot.withinss})
plot(1:k.max, wss,
       type="b", pch = 19, frame = FALSE, 
       xlab="Number of clusters K",
       ylab="Total within-clusters sum of squares")
abline(v = 3, lty =2)

Silhouette analysis

set.seed(322)
k.max <- 10
data <- feature_vector_training
nrow(data)
[1] 3322
sil <- rep(0, k.max)
# Compute the average silhouette width for 
# k = 2 to k = 15
for(i in 2:k.max){
  km.res <- kmeans(data, centers = i, nstart = 25)
  ss <- silhouette(km.res$cluster, dist(data))
  sil[i] <- mean(ss[, 3])
}
# Plot the  average silhouette width
plot(1:k.max, sil, type = "b", pch = 19, 
     frame = FALSE, xlab = "Number of clusters k")
abline(v = which.max(sil), lty = 2)

Useful functions

cold_start_data <- function(training.sampled,testing,settings){
  library(doParallel)
  cl <- makeCluster(2)
  registerDoParallel(cl)
  size_training <- nrow(training.sampled)
  split_size_training = size_training / 200
  testing_result = data.frame(numeric(nrow(testing)))
  
  count_random <- foreach(i=1:split_size_training) %dopar% {
    200 * i
  }
  metric <- numeric(split_size_training)
  metric_t <- numeric(split_size_training)
  #metric <- foreach(i=1:split_size_training) %do% {
  for(i in c(1:split_size_training)){
    #library(caret)
    #library(dplyr)
    count <- 200 * i
    aux_training_set <- training.sampled[c(1:count), ]#training[sample(size_training, count), ]
    clusters <- kmeans(aux_training_set[,-c(11,12,13,14)],3,nstart = 25)
    aux_training_set_cluster <- cbind(aux_training_set, cluster = clusters$cluster)
    result_vector <- numeric(nrow(testing))
    result_vector_trainning <- numeric(nrow(aux_training_set))
    
    for (j in c(1:3)){
      cluster_data <- dplyr::filter(aux_training_set_cluster, cluster == j)
      new_rfFit <- train(subclass ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl,
                 data = cluster_data,
                 metric="ROC",
                 method = "rf",
                 trControl = settings)
      #Testing predict
      predsrfprobs <- predict(new_rfFit,testing,type='prob')
      
      for (k in c(1:length(result_vector))){
        if(predsrfprobs$botnet[k] > 0.5){
          result_vector[k] <- result_vector[k] + 1
        }
        else{
          result_vector[k] <- result_vector[k] - 1
        }
      }
      
      #Trainning predict
      predsrfprobs_t <- predict(new_rfFit,aux_training_set,type='prob')
      for (k in c(1:length(result_vector_trainning))){
        if(predsrfprobs_t$botnet[k] > 0.5){
          result_vector_trainning[k] <- result_vector_trainning[k] + 1
        }
        else{
          result_vector_trainning[k] <- result_vector_trainning[k] - 1
        }
      }
    }
    a = ifelse(result_vector > 0,'botnet','normal')
    b <- ifelse(result_vector_trainning > 0,'botnet','normal')
    testing_result <- cbind(testing_result,'result' = result_vector)
    cm <- confusionMatrix(a,testing$subclass)
    metric[i] <- cm$byClass['F1']#cm$overall[1]
    
    cm_t <- confusionMatrix(b,aux_training_set$subclass)
    metric_t[i] <- cm_t$byClass['F1']
    #list('metric' = metric, 'metric_t' = metric_t)
  }
  output <- do.call(rbind, Map(data.frame, data_count=count_random, metric=metric))
  output_t <- do.call(rbind, Map(data.frame, data_count=count_random, metric=metric_t))
  list_result <- list('output' = output, 'output_t' = output_t, 'testing_result' = testing_result)
}
cold_start_data_only_rf <- function(training.sampled,testing,settings){
  size_training <- nrow(training.sampled)
  split_size_training = size_training / 200
  testing_result = data.frame(numeric(nrow(testing)))
  
  count_random <- foreach(i=1:split_size_training) %dopar% {
    200 * i
  }
  metric <- numeric(split_size_training)
  metric_t <- numeric(split_size_training)
  #metric <- foreach(i=1:split_size_training) %do% {
  for(i in c(1:split_size_training)){
    #library(caret)
    #library(dplyr)
    count <- 200 * i
    aux_training_set <- training.sampled[c(1:count), ]#training[sample(size_training, count), ]
    new_rfFit <- train(subclass ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl,
                 data = aux_training_set,
                 metric="ROC",
                 method = "rf",
                 trControl = settings)
    #Testing predict
    predsrfprobs <- predict(new_rfFit,testing,type='prob')
    predsrf <- ifelse(predsrfprobs$botnet >=0.5,'botnet','normal')
    cm <- confusionMatrix(predsrf,testing$subclass)
    metric[i] <- cm$byClass['F1']
    
    
    #Trainning predict
    predsrfprobs_t <- predict(new_rfFit,aux_training_set,type='prob')
    predsrf_t <- ifelse(predsrfprobs_t$botnet >= 0.5,'botnet','normal')
    cm_t <- confusionMatrix(predsrf_t,aux_training_set$subclass)
    metric_t[i] <- cm_t$byClass['F1']
    
  }
  output <- do.call(rbind, Map(data.frame, data_count=count_random, metric=metric))
  output_t <- do.call(rbind, Map(data.frame, data_count=count_random, metric=metric_t))
  list_result <- list('output' = output, 'output_t' = output_t, 'testing_result' = testing_result)
}
generate_data_noisy <- function(dataset, porcent){
  list_aux <- sample(nrow(dataset) ,porcent)
  noisy_data_sample <- dataset[list_aux,]
  no_noisy_data_sample <- dataset[-list_aux,]
  
  noisy_data_sample_b <- noisy_data_sample %>% filter(class == 'Botnet')
  noisy_data_sample_n <- noisy_data_sample %>% filter(class == 'Normal')
  
  noisy_data_sample_b$class <- as.character(noisy_data_sample_b$class)
  noisy_data_sample_b$class[noisy_data_sample_b$class == 'Botnet'] <- 'Normal'
  noisy_data_sample_b$class <- as.factor(noisy_data_sample_b$class)
  
  noisy_data_sample_n$class <- as.character(noisy_data_sample_n$class)
  noisy_data_sample_n$class[noisy_data_sample_n$class == 'Normal'] <- 'Botnet'
  noisy_data_sample_n$class <- as.factor(noisy_data_sample_n$class)
  
  noisy_data <- rbind(noisy_data_sample_b, noisy_data_sample_n)
  training_noisy <- rbind(no_noisy_data_sample,noisy_data)
  training_noisy <- training_noisy[sample(nrow(training_noisy),nrow(training_noisy)),]
  return(training_noisy)
}
get_ELA_measure <- function(A0, Ax){
  RLA <- (A0 - Ax) / A0
  FA0 <- (100 - A0) / A0
  ELA <- RLA + FA0
  return(ELA)
}
randomForest_performace <- function(training_data, testing_data){
  rfFit <- train(class ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl,
                 data = training_data,
                 metric="ROC",
                 method = "rf",
                 trControl = settings)
  predsrfprobs <- predict(rfFit,testing_data,type='prob')
  predsrf <- ifelse(predsrfprobs$Botnet >=0.5,'Botnet','Normal')
  cm <- confusionMatrix(predsrf,testing_data$class)
  result <- cm$byClass
  return(result)
}
training
testing

Data training partitions: cold start study

Iteration #1

output_1 <- result$output
output_t_1 <- result$output_t
output_1

gg <- ggplot(data = output_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

first_training_sample <- training.sampled_1[1:200,]
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

testing_result <- result$testing_result
#testing_result
gg <- ggplot(data = output_t_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

output_t_aux_1 <- output_t_1
names(output_t_aux_1) <- c('data_count_t','metric_t')
output_result_1 <- cbind(output_1,output_t_aux_1)
gg <- ggplot(data = output_result_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

Iteration #2

output_2 <- result_2$output
output_t_2 <- result_2$output_t
output_2

gg <- ggplot(data = output_2)
  gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
    labs(title="Random Forest through data training size", 
         #subtitle="Drawn from Long Data format", 
         caption="Source: CTU-13", 
         y="F1 Score", 
         color=NULL)

first_training_sample <- training.sampled_2[1:200,]
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

testing_result_2 <- result$testing_result
#testing_result
gg <- ggplot(data = output_t_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

output_t_aux_2 <- output_t_2
names(output_t_aux_2) <- c('data_count_t','metric_t')
output_result_2 <- cbind(output_2,output_t_aux_2)
gg <- ggplot(data = output_result_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

Iteration #3

output_3 <- result_3$output
output_t_3 <- result_3$output_t
output_3

gg <- ggplot(data = output_3)
  gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
    labs(title="Random Forest through data training size", 
         #subtitle="Drawn from Long Data format", 
         caption="Source: CTU-13", 
         y="F1 Score", 
         color=NULL)

first_training_sample <- training.sampled_3[1:200,]
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

testing_result_3 <- result$testing_result
#testing_result
gg <- ggplot(data = output_t_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

output_t_aux_3 <- output_t_3
names(output_t_aux_3) <- c('data_count_t','metric_t')
output_result_3 <- cbind(output_3,output_t_aux_3)
gg <- ggplot(data = output_result_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

Iteration #4

output_4 <- result_4$output
output_t_4 <- result_4$output_t
output_4

gg <- ggplot(data = output_4)
  gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
    labs(title="Random Forest through data training size", 
         #subtitle="Drawn from Long Data format", 
         caption="Source: CTU-13", 
         y="F1 Score", 
         color=NULL)

first_training_sample <- training.sampled_4[1:200,]
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

testing_result_4 <- result$testing_result
#testing_result
gg <- ggplot(data = output_t_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

output_t_aux_4 <- output_t_4
names(output_t_aux_4) <- c('data_count_t','metric_t')
output_result_4 <- cbind(output_4,output_t_aux_4)
gg <- ggplot(data = output_result_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

Iteration #5

output_5 <- result_5$output
output_t_5 <- result_5$output_t
output_5

gg <- ggplot(data = output_5)
  gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
    labs(title="Random Forest through data training size", 
         #subtitle="Drawn from Long Data format", 
         caption="Source: CTU-13", 
         y="F1 Score", 
         color=NULL)

first_training_sample <- training.sampled_5[1:200,]
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

testing_result_5 <- result$testing_result
#testing_result
gg <- ggplot(data = output_t_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

output_t_aux_5 <- output_t_5
names(output_t_aux_5) <- c('data_count_t','metric_t')
output_result_5 <- cbind(output_5,output_t_aux_5)
gg <- ggplot(data = output_result_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

Data training partitions: cold start study (simple Random Forest)

Iteration #1

rf_output_1 <- rf_result_1$output
rf_output_t_1 <- rf_result_1$output_t
rf_output_1
gg <- ggplot(data = rf_output_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

rf_first_training_sample <- rf_training.sampled_1[1:200,]
ggplot(rf_first_training_sample) + geom_bar(aes(subclass))

rf_class_distribution <- rf_first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(rf_first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

#rf_testing_result_1 <- rf_result_1$testing_result
#testing_result
gg <- ggplot(data = rf_output_t_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

rf_output_t_aux_1 <- rf_output_t_1
names(rf_output_t_aux_1) <- c('data_count_t','metric_t')
rf_output_result_1 <- cbind(rf_output_1,rf_output_t_aux_1)
gg <- ggplot(data = rf_output_result_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

Iteration #2

rf_output_2 <- rf_result_2$output
rf_output_t_2 <- rf_result_2$output_t
rf_output_2
gg <- ggplot(data = rf_output_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

rf_first_training_sample <- rf_training.sampled_2[1:200,]
ggplot(rf_first_training_sample) + geom_bar(aes(subclass))

rf_class_distribution <- rf_first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(rf_first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

#rf_testing_result_2 <- rf_result_2$testing_result
#testing_result
gg <- ggplot(data = rf_output_t_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

rf_output_t_aux_2 <- rf_output_t_2
names(rf_output_t_aux_2) <- c('data_count_t','metric_t')
rf_output_result_2 <- cbind(rf_output_2,rf_output_t_aux_2)
gg <- ggplot(data = rf_output_result_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

Iteration #3

rf_output_3 <- rf_result_3$output
rf_output_t_3 <- rf_result_3$output_t
rf_output_3
gg <- ggplot(data = rf_output_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

rf_first_training_sample <- rf_training.sampled_3[1:200,]
ggplot(rf_first_training_sample) + geom_bar(aes(subclass))

rf_class_distribution <- rf_first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(rf_first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

#rf_testing_result_2 <- rf_result_2$testing_result
#testing_result
gg <- ggplot(data = rf_output_t_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

rf_output_t_aux_3 <- rf_output_t_3
names(rf_output_t_aux_3) <- c('data_count_t','metric_t')
rf_output_result_3 <- cbind(rf_output_3,rf_output_t_aux_3)
gg <- ggplot(data = rf_output_result_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

Iteration #4

rf_output_4 <- rf_result_4$output
rf_output_t_4 <- rf_result_4$output_t
rf_output_4
gg <- ggplot(data = rf_output_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

rf_first_training_sample <- rf_training.sampled_4[1:200,]
ggplot(rf_first_training_sample) + geom_bar(aes(subclass))

rf_class_distribution <- rf_first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(rf_first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

#rf_testing_result_2 <- rf_result_2$testing_result
#testing_result
gg <- ggplot(data = rf_output_t_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

rf_output_t_aux_4 <- rf_output_t_4
names(rf_output_t_aux_4) <- c('data_count_t','metric_t')
rf_output_result_4 <- cbind(rf_output_4,rf_output_t_aux_4)
gg <- ggplot(data = rf_output_result_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

Iteration #5

rf_output_5 <- rf_result_5$output
rf_output_t_5 <- rf_result_5$output_t
rf_output_5
gg <- ggplot(data = rf_output_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

rf_first_training_sample <- rf_training.sampled_5[1:200,]
ggplot(rf_first_training_sample) + geom_bar(aes(subclass))

rf_class_distribution <- rf_first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(rf_first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

#rf_testing_result_2 <- rf_result_2$testing_result
#testing_result
gg <- ggplot(data = rf_output_t_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

rf_output_t_aux_5 <- rf_output_t_5
names(rf_output_t_aux_5) <- c('data_count_t','metric_t')
rf_output_result_5 <- cbind(rf_output_5,rf_output_t_aux_5)
gg <- ggplot(data = rf_output_result_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="F1 Score", 
       color=NULL)

Studies Samples

first_training_sample <- training.sampled[1:200,]
first_training_sample
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

part 2

set.seed(206)
library(doParallel)
cl <- makeCluster(2)
registerDoParallel(cl)
size_training <- nrow(training)
split_size_training = size_training / 200
count_random <- foreach(i=1:split_size_training) %dopar% {
  200 * i
}
training.sampled <- training[sample(size_training, size_training), ]
metric <- foreach(i=1:split_size_training) %do% {
  #library(caret)
  count <- 200 * i
  aux_training_set <- training.sampled[c(1:count), ]#training[sample(size_training, count), ]
  clusters <- kmeans(aux_training_set[,-c(11,12,13,14)],3,nstart = 25)
  aux_training_set_cluster <- cbind(aux_training_set, cluster = clusters$cluster)
  result_vector <- numeric(nrow(testing))
  for (j in c(1:3)){
    cluster_data <- filter(aux_training_set_cluster, cluster == j)
    new_rfFit <- train(class ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl,
               data = cluster_data,
               metric="ROC",
               method = "rf",
               trControl = ctrl_fast)
    predsrfprobs <- predict(new_rfFit,testing,type='prob')
    for (k in c(1:length(result_vector))){
      if(predsrfprobs$Botnet[k] > 0.5){
        result_vector[k] <- result_vector[k] + 1
      }
      else{
        result_vector[k] <- result_vector[k] - 1
      }
    }
    
  }
  a = ifelse(result_vector > 0,'Botnet','Normal')
  cm <- confusionMatrix(a,testing$class)
  metric <- cm$byClass['F1']#cm$overall[1]
  metric
  
}

output <- do.call(rbind, Map(data.frame, data_count=count_random, metric=metric))
output
gg <- ggplot(data = output)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + 
  labs(title="Random Forest through data training size", 
       #subtitle="Drawn from Long Data format", 
       caption="Source: CTU-13", 
       y="Accuracy", 
       color=NULL)
cluster_data

Test with only one

set.seed(226)
size_training <- nrow(training)
training.sampled <- training[sample(size_training, size_training), ]

aux_training_set <- training.sampled[c(1:200), ]#training[sample(size_training, 200), ]
clusters <- kmeans(aux_training_set[,-c(11,12,13,14)],3,nstart = 25)
aux_training_set_cluster <- cbind(aux_training_set, cluster = clusters$cluster)
result_vector <- numeric(nrow(testing))
result_vector_trainning <- numeric(nrow(aux_training_set))

for (j in c(1:3)){
  cluster_data <- aux_training_set_cluster %>% filter(cluster == j)
  new_rfFit <- train(subclass ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl,
               data = cluster_data,
               metric="ROC",
               method = "rf",
               trControl = ctrl_fast)
  predsrfprobs <- predict(new_rfFit,testing,type='prob')
  for (k in c(1:length(result_vector))){
    if(predsrfprobs$botnet[k] > 0.5){
      result_vector[k] <- result_vector[k] + 1
    }
    else{
      result_vector[k] <- result_vector[k] - 1
    }
  }
  
  #Trainning predict
  predsrfprobs_t <- predict(new_rfFit,aux_training_set,type='prob')
  for (k in c(1:length(result_vector_trainning))){
    if(predsrfprobs_t$botnet[k] > 0.5){
      result_vector_trainning[k] <- result_vector_trainning[k] + 1
    }
    else{
      result_vector_trainning[k] <- result_vector_trainning[k] - 1
    }
  }
}

a = ifelse(result_vector > 0,'botnet','normal')
b <- ifelse(result_vector_trainning > 0,'botnet','normal')
cm <- confusionMatrix(a,testing$subclass)
metric <- cm$byClass['F1']#cm$overall[1]
metric
cm_t <- confusionMatrix(b,aux_training_set$subclass)
metric_t <- cm_t$byClass['F1']
metric_t

Sample examples

set.seed(556)
a = c(1,2,3,4,5,6,7,8,9)
r <- sample(9,3)
a[r]
r2 <- sample(9,3)
a[r2]
#testing_result
testing_result.bkp <- testing_result
testing_result
names_aux <- foreach(i=1:(nrow(training)/200)) %do% {
    iteration <- 200 * i
    paste('size_',toString(iteration),sep = "")
}
testing_result_names <- unlist(names_aux, use.names=FALSE)
testing_result <- testing_result[,c(-1)]
names(testing_result) <- testing_result_names
testing_result

testing_aux <- cbind(testing,testing_result)
testing_aux.bkp2 <- testing_aux
#write.table(testing_aux,file="testing_cluster_result.txt",sep="|", row.names = F)
testing_aux
sums <- rowSums(testing_aux[,-c(1:14)])
sums
testing_aux[,-c(1:14)]
testing_aux <- cbind(testing_aux,sums)
testing_aux
testing_aux_result <- testing_aux %>% group_by(class) %>% summarise(n = n(), sums = sum(sums)) %>% arrange(desc(sums))
testing_aux_result

graph_testing_result <- ggplot(testing_aux_result[-c(1,nrow(testing_aux_result)),])
graph_testing_result + geom_point(aes(class,sums)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

feature_vectors_cleaned

library(gridExtra)
pdf("data_output.pdf", height=11, width=8.5)
grid.table(feature_vectors_cleaned[1:20,])
dev.off()

testing_result.bkp
testing_aux.bkp2
testing_aux_result

rusty_data_result <- testing_aux.bkp2
rusty_data_result_short <- rusty_data_result[,-c(1:11,14)]
rusty_data_result_short[,-c(1,2)]
rusty_data_result_short$pos <- rowSums(rusty_data_result_short[,-c(1,2)] > 0)
rusty_data_result_short$neg <- rowSums(rusty_data_result_short[,-c(1,2)] < 0)
rusty_data_result_short_cleaned <- rusty_data_result_short[,c(1,2,46,47)]
rusty_data_result_short_cleaned
rusty_data_result_short_cleaned_result <- rusty_data_result_short_cleaned %>% mutate(good = ifelse(subclass == 'normal',neg,pos))
rusty_data_result_short_cleaned_result <- rusty_data_result_short_cleaned_result %>% mutate(bad = ifelse(subclass == 'normal',pos,neg))
rusty_data_result_short_cleaned_result %>% group_by(port) %>% summarise(n=n(),good = sum(good),bad = sum(bad)) %>% arrange(desc(n))

data_botnet_port <- rusty_data_result_short_cleaned_result %>% filter(subclass == 'botnet')
data_normal_port <- rusty_data_result_short_cleaned_result %>% filter(subclass == 'normal')
data_botnet_port_result <-  data_botnet_port %>% group_by(port) %>% summarise(n=n(),good = sum(good),bad = sum(bad)) %>% arrange(desc(n))
data_normal_port_result <- data_normal_port %>% group_by(port) %>% summarise(n=n(),good = sum(good),bad = sum(bad)) %>% arrange(desc(n))
data_botnet_port_result
data_normal_port_result

ggplot(data = data_botnet_port_result) + 
  geom_bar(mapping = aes(x = port, fill = clarity))

#write.table(data_botnet_port_result,file="data_botnet_port.txt",sep="|", row.names = F)
library(reshape2)
data <- data_botnet_port_result
data$port <- as.factor(data$port)

melt(data[,c(1,3,4)])

ggplot(melt(data[,c(1,3,4)]))+
  geom_col(aes(x=port,y=value,fill=variable))+
  #theme_bw()+
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Making Noisy data (training_noisy: dataset to train with 20% of noisy)

set.seed(101) 
training.bkp <- training
noisy_data <- training

porcent <- nrow(training) / 5
training_noisy <- generate_data_noisy(noisy_data,porcent)
nrow(training)
nrow(training_noisy)
cm$overall[1]
 Accuracy 
0.9222042 

Robustness Analisys(one simple iteration)

rf_measures_result
 [1]         NA 0.92433697         NA 0.92199688         NA 0.92043682         NA 0.91341654
 [9]         NA 0.91809672         NA 0.91107644         NA 0.91575663         NA 0.91809672
[17]         NA 0.90951638         NA 0.91185647         NA 0.91107644         NA 0.90249610
[25]         NA 0.90405616         NA 0.90171607         NA 0.87051482         NA 0.88845554
[33]         NA 0.86739470         NA 0.84243370         NA 0.85179407         NA 0.78549142
[41]         NA 0.72074883         NA 0.74726989         NA 0.66458658         NA 0.58346334
[49]         NA 0.48673947         NA 0.40483619         NA 0.30109204         NA 0.22308892
[57]         NA 0.24258970         NA 0.18798752         NA 0.14664587         NA 0.13182527
[65]         NA 0.12792512         NA 0.12090484         NA 0.12792512         NA 0.11310452
[73]         NA 0.09438378         NA 0.09438378         NA 0.09282371         NA 0.08346334
[81]         NA 0.09516381         NA 0.09438378         NA 0.08658346         NA 0.08112324
[89]         NA 0.07878315

Robustness Analisys: ploting accuracy

ggplotly(g)
We recommend that you use the dev version of ggplot2 with `ggplotly()`
Install it with: `devtools::install_github('hadley/ggplot2')`
`geom_smooth()` using method = 'loess'

Robustness Analisys: ploting ELA measure

index <- seq(2,90,2)
measure_result <- ela_measures_result[index]
ela_measure_data <- data.frame(index,measure_result)
names(ela_measure_data) <- c('noise_porcent','ela_measure')

ggplot(ela_measure_data) + geom_point(mapping = aes(x = noise_porcent, y = ela_measure)) + geom_smooth(mapping = aes(x = noise_porcent, y = ela_measure))

Robustness Analisys(30 iterations)

Cosine Similarity

training
testing
prediction_vector <- testing[1,]
prediction_vector <- as.vector(as.matrix(prediction_vector))
result <- prediction_by_similarity(training,prediction_vector,100)
result

result <- c()
for(i in 1:nrow(testing)){
  prediction_vector <- testing[i,]
  prediction_vector <- as.vector(as.matrix(prediction_vector))
  result[i] <- prediction_by_similarity(training,prediction_vector,100)
}
vector_result <- unlist(result)
cm <- confusionMatrix(vector_result,testing$class)
cm

cs_data.result <- data.frame(rf_result_5$output$data_count)
Warning message:
In str.default(val) : 'object' does not have valid levels()
#for(i in c(1:1)){
  current_seed <- 226 #+ i
  set.seed(current_seed)
  
  size_training <- nrow(training)
  cs_training.sampled_current <- training[sample(size_training, size_training), ]
  split_size_training = size_training / 200
  metric <- numeric(split_size_training)
  for(j in 1:split_size_training){
    count <- 200 * j
    aux_training_set <- cs_training.sampled_current[c(1:count), ]
    result <- c()
    for(k in 1:nrow(testing)){
      prediction_vector <- testing[k,]
      prediction_vector <- as.vector(as.matrix(prediction_vector))
      output_result <- prediction_by_similarity(aux_training_set,prediction_vector,101)
      result[k] <- output_result
    }
    vector_result <- unlist(result)
    cm <- confusionMatrix(vector_result,testing$class)
    metric[j] <- cm$byClass['F1']
    
  }
  cs_data.result <- cbind(cs_data.result, metric)
Error in data.frame(..., check.names = FALSE) : 
  arguments imply differing number of rows: 16, 51
LS0tCnRpdGxlOiAiQ0FJJ3MgZXhwZXJpbWVudHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyBMaWJyYXJ5IEVudmlyb25tZW50CmBgYHtyfQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkodGlkeXZlcnNlKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHN0cmluZ3IpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoSVNMUikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShjYXJldCkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShkb01DKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHBsb3RseSkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShzdHJpbmdyKSkKcmVnaXN0ZXJEb01DKGNvcmVzPTQpCmBgYAoKIyMjIExvYWQgYW5kIHByb2Nlc3NpbmcgZGF0YSBjdHUxMyBjbGVhbmVkCmBgYHtyfQpteURhdGFfY2xlYW5lZCA8LSByZWFkLmNzdignL2hvbWUvamd1ZXJyYS9kYXRhc2V0cy9jdHUxMy5sYWJlbGVkLmNsZWFuZWQnLCBzdHJpbmdzQXNGYWN0b3JzID0gRiwgc2VwID0gJ3wnKQpteURhdGFfY2xlYW5lZC5ia3AgPSBteURhdGFfY2xlYW5lZApteURhdGFfY2xlYW5lZAoKI1BlcmlvZGljaXR5Cm15RGF0YV9jbGVhbmVkID0gbXlEYXRhX2NsZWFuZWQgJT4lIG11dGF0ZShzdHJvbmdfcCA9IHN0cl9jb3VudChTdGF0ZSwnW2EtaV0nKSkKbXlEYXRhX2NsZWFuZWQgPSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKHdlYWtfcCA9IHN0cl9jb3VudChTdGF0ZSwnW0EtSV0nKSkKbXlEYXRhX2NsZWFuZWQgPSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKHdlYWtfbnAgPSBzdHJfY291bnQoU3RhdGUsJ1tyLXpdJykpCm15RGF0YV9jbGVhbmVkID0gbXlEYXRhX2NsZWFuZWQgJT4lIG11dGF0ZShzdHJvbmdfbnAgPSBzdHJfY291bnQoU3RhdGUsJ1tSLVpdJykpCiNEdXJhdGlvbgpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoZHVyYXRpb25fcyA9IHN0cl9jb3VudChTdGF0ZSwnKGF8QXxyfFJ8MXxkfER8dXxVfDR8Z3xHfHh8WHw3KScpKQpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoZHVyYXRpb25fbSA9IHN0cl9jb3VudChTdGF0ZSwnKGJ8QnxzfFN8MnxlfEV8dnxWfDV8aHxIfHl8WXw4KScpKQpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoZHVyYXRpb25fbCA9IHN0cl9jb3VudChTdGF0ZSwnKGN8Q3x0fFR8M3xmfEZ8d3xXfDZ8aXxJfHp8Wnw5KScpKQojU2l6ZQpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoc2l6ZV9zID0gc3RyX2NvdW50KFN0YXRlLCdbYS1jXScpICsgc3RyX2NvdW50KFN0YXRlLCdbQS1DXScpICsgc3RyX2NvdW50KFN0YXRlLCdbci10XScpICsgc3RyX2NvdW50KFN0YXRlLCdbUi1UXScpICsgc3RyX2NvdW50KFN0YXRlLCdbMS0zXScpKQpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoc2l6ZV9tID0gc3RyX2NvdW50KFN0YXRlLCdbZC1mXScpICsgc3RyX2NvdW50KFN0YXRlLCdbRC1GXScpICsgc3RyX2NvdW50KFN0YXRlLCdbdS13XScpICsgc3RyX2NvdW50KFN0YXRlLCdbVS1XXScpICsgc3RyX2NvdW50KFN0YXRlLCdbNC02XScpKQpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoc2l6ZV9sID0gc3RyX2NvdW50KFN0YXRlLCdbZy1pXScpICsgc3RyX2NvdW50KFN0YXRlLCdbRy1JXScpICsgc3RyX2NvdW50KFN0YXRlLCdbeC16XScpICsgc3RyX2NvdW50KFN0YXRlLCdbWC1aXScpICsgc3RyX2NvdW50KFN0YXRlLCdbNy05XScpKQoKI1BlcmlvZGljaXR5ICUKbXlEYXRhX2NsZWFuZWQgPC0gbXlEYXRhX2NsZWFuZWQgJT4lIG11dGF0ZShzdHJvbmdfcCA9IChzdHJvbmdfcCAvIG1vZGVsc2l6ZSkpCm15RGF0YV9jbGVhbmVkIDwtIG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUod2Vha19wID0gKHdlYWtfcCAvIG1vZGVsc2l6ZSkpCm15RGF0YV9jbGVhbmVkIDwtIG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoc3Ryb25nX25wID0gKHN0cm9uZ19ucCAvIG1vZGVsc2l6ZSkpCm15RGF0YV9jbGVhbmVkIDwtIG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUod2Vha19ucCA9ICh3ZWFrX25wIC8gbW9kZWxzaXplKSkKI0R1cmF0aW9uICUKbXlEYXRhX2NsZWFuZWQgPC0gbXlEYXRhX2NsZWFuZWQgJT4lIG11dGF0ZShkdXJhdGlvbl9zID0gKGR1cmF0aW9uX3MgLyBtb2RlbHNpemUpKQpteURhdGFfY2xlYW5lZCA8LSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKGR1cmF0aW9uX20gPSAoZHVyYXRpb25fbSAvIG1vZGVsc2l6ZSkpCm15RGF0YV9jbGVhbmVkIDwtIG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoZHVyYXRpb25fbCA9IChkdXJhdGlvbl9sIC8gbW9kZWxzaXplKSkKI1NpemUgJQpteURhdGFfY2xlYW5lZCA8LSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKHNpemVfcyA9IChzaXplX3MgLyBtb2RlbHNpemUpKQpteURhdGFfY2xlYW5lZCA8LSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKHNpemVfbSA9IChzaXplX20gLyBtb2RlbHNpemUpKQpteURhdGFfY2xlYW5lZCA8LSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKHNpemVfbCA9IChzaXplX2wgLyBtb2RlbHNpemUpKQoKI01ha2luZyBmZWF0dXJlIHZlY3RvcnMKZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQgPSBteURhdGFfY2xlYW5lZFssYygnc3Ryb25nX3AnLCd3ZWFrX3AnLCd3ZWFrX25wJywnc3Ryb25nX25wJywnZHVyYXRpb25fcycsJ2R1cmF0aW9uX20nLCdkdXJhdGlvbl9sJywnc2l6ZV9zJywnc2l6ZV9tJywnc2l6ZV9sJywnbW9kZWxzaXplJywnbGFiZWwnLCdjbGFzcycsJ3BvcnQnLCdwcm90bycpXQpuYW1lcyhmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCkgPSBjKCJzcCIsIndwIiwid25wIiwic25wIiwiZHMiLCJkbSIsImRsIiwic3MiLCJzbSIsInNsIiwibW9kZWxzaXplIiwiY2xhc3MiLCJzdWJjbGFzcyIsInBvcnQiLCJwcm90byIpCmZlYXR1cmVfdmVjdG9yc19jbGVhbmVkJGNsYXNzID0gZmFjdG9yKGZlYXR1cmVfdmVjdG9yc19jbGVhbmVkJGNsYXNzKQpmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCRzdWJjbGFzcyA9IGZhY3RvcihmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCRzdWJjbGFzcykKZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQkcHJvdG8gPSBmYWN0b3IoZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQkcHJvdG8pCgpmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZAoKYGBgCgojIyMgUmVtb3ZpbmcgZXhjZXNpdmUgQm90bmV0IGFuZCBOb3JtYWwgY2xhc3MoTWFraW5nIHRoZSBkYXRhc2V0IG1vcmUgZXF1aXRhYmxlKQpgYGB7cn0KZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQuYmtwIDwtIGZlYXR1cmVfdmVjdG9yc19jbGVhbmVkCmZlYXR1cmVfdmVjdG9yc19jbGVhbmVkICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuPW4oKSkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWRfYXV4X2JvdG5ldCA8LSBmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCAlPiUgZmlsdGVyKGNsYXNzID09ICdCb3RuZXQtVENQLVNNVFAtQXR0ZW1wdC1TUEFNJykKZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWRfYXV4X25vcm1hbCA8LSBmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCAlPiUgZmlsdGVyKGNsYXNzID09ICdOb3JtYWwtVENQLUhUVFAnKQpmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZF9hdXhfYm90bmV0CmZlYXR1cmVfdmVjdG9yc19jbGVhbmVkX2F1eF9ub3JtYWwKCmZlYXR1cmVfdmVjdG9yc19jbGVhbmVkX2F1eF9yZXN0IDwtIGZlYXR1cmVfdmVjdG9yc19jbGVhbmVkICU+JSBmaWx0ZXIoY2xhc3MgIT0gJ0JvdG5ldC1UQ1AtU01UUC1BdHRlbXB0LVNQQU0nKSAlPiUgZmlsdGVyKGNsYXNzICE9ICdOb3JtYWwtVENQLUhUVFAnKQpmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZF9hdXhfcmVzdCAlPiUgZ3JvdXBfYnkoY2xhc3MpICU+JSBzdW1tYXJpc2Uobj1uKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCmF1eDEgPC0gcmJpbmQoZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWRfYXV4X2JvdG5ldFsxOjUwMCxdLGZlYXR1cmVfdmVjdG9yc19jbGVhbmVkX2F1eF9ub3JtYWxbMTo1MDAsXSkKYXV4MSAlPiUgZ3JvdXBfYnkoY2xhc3MpICU+JSBzdW1tYXJpc2Uobj1uKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCmF1eCA8LSByYmluZChmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZF9hdXhfcmVzdCxhdXgxKQphdXggJT4lIGdyb3VwX2J5KGNsYXNzKSAlPiUgc3VtbWFyaXNlKG49bigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCA8LSBhdXgKYGBgCgojIyMgQ3JlYXRlIHRyYWluaW5nIHNldCBhbmQgdGVzdHNldApgYGB7cn0Kc2V0LnNlZWQoMjEyKQp0cmFpbkluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQkc3ViY2xhc3MsIHA9MC43MCwgbGlzdD1GQUxTRSkKZGF0YV90cmFpbmluZyA8LSBmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZFsgdHJhaW5JbmRleCxdCmRhdGFfdGVzdGluZyA8LSBmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZFstdHJhaW5JbmRleCxdCgojZGF0YV90cmFpbiA9IGRhdGFfdHJhaW4gJT4lIGZpbHRlcihsZW5ndGg+NSkKdHJhaW4gPC0gdXBTYW1wbGUoeCA9IGRhdGFfdHJhaW5pbmcsICB5ID0gZGF0YV90cmFpbmluZyRzdWJjbGFzcywgeW5hbWU9ImNsYXNzIikKCnRyYWluaW5nIDwtIHRyYWluWywtYygxMSwxNildCnRlc3RpbmcgPC0gZGF0YV90ZXN0aW5nWywtYygxMSldCnRyYWluaW5nCnRlc3RpbmcKCm5yb3codHJhaW5pbmcpCm5yb3coZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQpCgpgYGAKCiMjIyBUcmFpbmluZyBjb25maWd1cmF0aW9uCmBgYHtyfQpjdHJsX2Zhc3QgPC0gdHJhaW5Db250cm9sKG1ldGhvZD0iY3YiLCAKICAgICAgICAgICAgICAgICAgICAgcmVwZWF0cz0yLAogICAgICAgICAgICAgICAgICAgICBudW1iZXI9MTAsIAogICAgICAgICAgICAgICAgICAgICBzdW1tYXJ5RnVuY3Rpb249dHdvQ2xhc3NTdW1tYXJ5LAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlSXRlcj1ULAogICAgICAgICAgICAgICAgICAgICBjbGFzc1Byb2JzPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGFsbG93UGFyYWxsZWwgPSBUUlVFKSAgCmBgYAoKIyMjIEV4cGVyaW1lbnQgMQojIyBDcmVhdGlvbiBvZiBjbHVzdGVyIGFuZCBrIHBhcmFtZXRlcnMgYW5hbHlzaXMKYGBge3J9CmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShjbHVzdGVyKQpsaWJyYXJ5KE5iQ2x1c3QpCmZlYXR1cmVfdmVjdG9yX3RyYWluaW5nID0gdHJhaW5pbmdbLC1jKDExLDEyLDEzLDE0KV0KIyBLLW1lYW5zIGNsdXN0ZXJpbmcKc2V0LnNlZWQoMzIxKQoja20ucmVzIDwtIGttZWFucyhmZWF0dXJlX3ZlY3Rvcl90cmFpbmluZywgMywgbnN0YXJ0ID0gMjUpCmttLnJlcyA8LSBrbWVhbnMoZmVhdHVyZV92ZWN0b3JfdHJhaW5pbmcsIDcsIG5zdGFydCA9IDI1KQojIGstbWVhbnMgZ3JvdXAgbnVtYmVyIG9mIGVhY2ggb2JzZXJ2YXRpb24Ka20ucmVzJGNsdXN0ZXIKCiMgVmlzdWFsaXplIGstbWVhbnMgY2x1c3RlcnMKZnZpel9jbHVzdGVyKGttLnJlcywgZGF0YSA9IGZlYXR1cmVfdmVjdG9yX3RyYWluaW5nLCBnZW9tID0gInBvaW50IiwKICAgICAgICAgICAgIHN0YW5kID0gRkFMU0UsIGVsbGlwc2UudHlwZSA9ICJub3JtIikKYGBgCiMjIyBFbGJvdyBhbmFseXNpcwpgYGB7cn0Kc2V0LnNlZWQoMzIxKQojIENvbXB1dGUgYW5kIHBsb3Qgd3NzIGZvciBrID0gMiB0byBrID0gMTUKay5tYXggPC0gMTUgIyBNYXhpbWFsIG51bWJlciBvZiBjbHVzdGVycwpkYXRhIDwtIGZlYXR1cmVfdmVjdG9yX3RyYWluaW5nCndzcyA8LSBzYXBwbHkoMTprLm1heCwgCiAgICAgICAgZnVuY3Rpb24oayl7a21lYW5zKGRhdGEsIGssIG5zdGFydD0xMCApJHRvdC53aXRoaW5zc30pCnBsb3QoMTprLm1heCwgd3NzLAogICAgICAgdHlwZT0iYiIsIHBjaCA9IDE5LCBmcmFtZSA9IEZBTFNFLCAKICAgICAgIHhsYWI9Ik51bWJlciBvZiBjbHVzdGVycyBLIiwKICAgICAgIHlsYWI9IlRvdGFsIHdpdGhpbi1jbHVzdGVycyBzdW0gb2Ygc3F1YXJlcyIpCmFibGluZSh2ID0gMywgbHR5ID0yKQpgYGAKIyMgU2lsaG91ZXR0ZSBhbmFseXNpcwpgYGB7cn0Kc2V0LnNlZWQoMzIyKQprLm1heCA8LSAxMApkYXRhIDwtIGZlYXR1cmVfdmVjdG9yX3RyYWluaW5nCm5yb3coZGF0YSkKc2lsIDwtIHJlcCgwLCBrLm1heCkKIyBDb21wdXRlIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggZm9yIAojIGsgPSAyIHRvIGsgPSAxNQoKZm9yKGkgaW4gMjprLm1heCl7CiAga20ucmVzIDwtIGttZWFucyhkYXRhLCBjZW50ZXJzID0gaSwgbnN0YXJ0ID0gMjUpCiAgc3MgPC0gc2lsaG91ZXR0ZShrbS5yZXMkY2x1c3RlciwgZGlzdChkYXRhKSkKICBzaWxbaV0gPC0gbWVhbihzc1ssIDNdKQp9CiMgUGxvdCB0aGUgIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aApwbG90KDE6ay5tYXgsIHNpbCwgdHlwZSA9ICJiIiwgcGNoID0gMTksIAogICAgIGZyYW1lID0gRkFMU0UsIHhsYWIgPSAiTnVtYmVyIG9mIGNsdXN0ZXJzIGsiKQphYmxpbmUodiA9IHdoaWNoLm1heChzaWwpLCBsdHkgPSAyKQoKYGBgCiMjIyBVc2VmdWwgZnVuY3Rpb25zCmBgYHtyfQpjb2xkX3N0YXJ0X2RhdGEgPC0gZnVuY3Rpb24odHJhaW5pbmcuc2FtcGxlZCx0ZXN0aW5nLHNldHRpbmdzKXsKICBsaWJyYXJ5KGRvUGFyYWxsZWwpCiAgY2wgPC0gbWFrZUNsdXN0ZXIoMikKICByZWdpc3RlckRvUGFyYWxsZWwoY2wpCiAgc2l6ZV90cmFpbmluZyA8LSBucm93KHRyYWluaW5nLnNhbXBsZWQpCiAgc3BsaXRfc2l6ZV90cmFpbmluZyA9IHNpemVfdHJhaW5pbmcgLyAyMDAKICB0ZXN0aW5nX3Jlc3VsdCA9IGRhdGEuZnJhbWUobnVtZXJpYyhucm93KHRlc3RpbmcpKSkKICAKICBjb3VudF9yYW5kb20gPC0gZm9yZWFjaChpPTE6c3BsaXRfc2l6ZV90cmFpbmluZykgJWRvcGFyJSB7CiAgICAyMDAgKiBpCiAgfQogIG1ldHJpYyA8LSBudW1lcmljKHNwbGl0X3NpemVfdHJhaW5pbmcpCiAgbWV0cmljX3QgPC0gbnVtZXJpYyhzcGxpdF9zaXplX3RyYWluaW5nKQogICNtZXRyaWMgPC0gZm9yZWFjaChpPTE6c3BsaXRfc2l6ZV90cmFpbmluZykgJWRvJSB7CiAgZm9yKGkgaW4gYygxOnNwbGl0X3NpemVfdHJhaW5pbmcpKXsKICAgICNsaWJyYXJ5KGNhcmV0KQogICAgI2xpYnJhcnkoZHBseXIpCiAgICBjb3VudCA8LSAyMDAgKiBpCiAgICBhdXhfdHJhaW5pbmdfc2V0IDwtIHRyYWluaW5nLnNhbXBsZWRbYygxOmNvdW50KSwgXSN0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgY291bnQpLCBdCiAgICBjbHVzdGVycyA8LSBrbWVhbnMoYXV4X3RyYWluaW5nX3NldFssLWMoMTEsMTIsMTMsMTQpXSwzLG5zdGFydCA9IDI1KQogICAgYXV4X3RyYWluaW5nX3NldF9jbHVzdGVyIDwtIGNiaW5kKGF1eF90cmFpbmluZ19zZXQsIGNsdXN0ZXIgPSBjbHVzdGVycyRjbHVzdGVyKQogICAgcmVzdWx0X3ZlY3RvciA8LSBudW1lcmljKG5yb3codGVzdGluZykpCiAgICByZXN1bHRfdmVjdG9yX3RyYWlubmluZyA8LSBudW1lcmljKG5yb3coYXV4X3RyYWluaW5nX3NldCkpCiAgICAKICAgIGZvciAoaiBpbiBjKDE6MykpewogICAgICBjbHVzdGVyX2RhdGEgPC0gZHBseXI6OmZpbHRlcihhdXhfdHJhaW5pbmdfc2V0X2NsdXN0ZXIsIGNsdXN0ZXIgPT0gaikKICAgICAgbmV3X3JmRml0IDwtIHRyYWluKHN1YmNsYXNzIH4gc3Ard3Ard25wK3NucCtkcytkbStkbCtzcytzbStzbCwKICAgICAgICAgICAgICAgICBkYXRhID0gY2x1c3Rlcl9kYXRhLAogICAgICAgICAgICAgICAgIG1ldHJpYz0iUk9DIiwKICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHNldHRpbmdzKQogICAgICAjVGVzdGluZyBwcmVkaWN0CiAgICAgIHByZWRzcmZwcm9icyA8LSBwcmVkaWN0KG5ld19yZkZpdCx0ZXN0aW5nLHR5cGU9J3Byb2InKQogICAgICAKICAgICAgZm9yIChrIGluIGMoMTpsZW5ndGgocmVzdWx0X3ZlY3RvcikpKXsKICAgICAgICBpZihwcmVkc3JmcHJvYnMkYm90bmV0W2tdID4gMC41KXsKICAgICAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSArIDEKICAgICAgICB9CiAgICAgICAgZWxzZXsKICAgICAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSAtIDEKICAgICAgICB9CiAgICAgIH0KICAgICAgCiAgICAgICNUcmFpbm5pbmcgcHJlZGljdAogICAgICBwcmVkc3JmcHJvYnNfdCA8LSBwcmVkaWN0KG5ld19yZkZpdCxhdXhfdHJhaW5pbmdfc2V0LHR5cGU9J3Byb2InKQogICAgICBmb3IgKGsgaW4gYygxOmxlbmd0aChyZXN1bHRfdmVjdG9yX3RyYWlubmluZykpKXsKICAgICAgICBpZihwcmVkc3JmcHJvYnNfdCRib3RuZXRba10gPiAwLjUpewogICAgICAgICAgcmVzdWx0X3ZlY3Rvcl90cmFpbm5pbmdba10gPC0gcmVzdWx0X3ZlY3Rvcl90cmFpbm5pbmdba10gKyAxCiAgICAgICAgfQogICAgICAgIGVsc2V7CiAgICAgICAgICByZXN1bHRfdmVjdG9yX3RyYWlubmluZ1trXSA8LSByZXN1bHRfdmVjdG9yX3RyYWlubmluZ1trXSAtIDEKICAgICAgICB9CiAgICAgIH0KICAgIH0KICAgIGEgPSBpZmVsc2UocmVzdWx0X3ZlY3RvciA+IDAsJ2JvdG5ldCcsJ25vcm1hbCcpCiAgICBiIDwtIGlmZWxzZShyZXN1bHRfdmVjdG9yX3RyYWlubmluZyA+IDAsJ2JvdG5ldCcsJ25vcm1hbCcpCiAgICB0ZXN0aW5nX3Jlc3VsdCA8LSBjYmluZCh0ZXN0aW5nX3Jlc3VsdCwncmVzdWx0JyA9IHJlc3VsdF92ZWN0b3IpCiAgICBjbSA8LSBjb25mdXNpb25NYXRyaXgoYSx0ZXN0aW5nJHN1YmNsYXNzKQogICAgbWV0cmljW2ldIDwtIGNtJGJ5Q2xhc3NbJ0YxJ10jY20kb3ZlcmFsbFsxXQogICAgCiAgICBjbV90IDwtIGNvbmZ1c2lvbk1hdHJpeChiLGF1eF90cmFpbmluZ19zZXQkc3ViY2xhc3MpCiAgICBtZXRyaWNfdFtpXSA8LSBjbV90JGJ5Q2xhc3NbJ0YxJ10KICAgICNsaXN0KCdtZXRyaWMnID0gbWV0cmljLCAnbWV0cmljX3QnID0gbWV0cmljX3QpCiAgfQogIG91dHB1dCA8LSBkby5jYWxsKHJiaW5kLCBNYXAoZGF0YS5mcmFtZSwgZGF0YV9jb3VudD1jb3VudF9yYW5kb20sIG1ldHJpYz1tZXRyaWMpKQogIG91dHB1dF90IDwtIGRvLmNhbGwocmJpbmQsIE1hcChkYXRhLmZyYW1lLCBkYXRhX2NvdW50PWNvdW50X3JhbmRvbSwgbWV0cmljPW1ldHJpY190KSkKICBsaXN0X3Jlc3VsdCA8LSBsaXN0KCdvdXRwdXQnID0gb3V0cHV0LCAnb3V0cHV0X3QnID0gb3V0cHV0X3QsICd0ZXN0aW5nX3Jlc3VsdCcgPSB0ZXN0aW5nX3Jlc3VsdCkKfQoKY29sZF9zdGFydF9kYXRhX29ubHlfcmYgPC0gZnVuY3Rpb24odHJhaW5pbmcuc2FtcGxlZCx0ZXN0aW5nLHNldHRpbmdzKXsKICBzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcuc2FtcGxlZCkKICBzcGxpdF9zaXplX3RyYWluaW5nID0gc2l6ZV90cmFpbmluZyAvIDIwMAogIHRlc3RpbmdfcmVzdWx0ID0gZGF0YS5mcmFtZShudW1lcmljKG5yb3codGVzdGluZykpKQogIAogIGNvdW50X3JhbmRvbSA8LSBmb3JlYWNoKGk9MTpzcGxpdF9zaXplX3RyYWluaW5nKSAlZG9wYXIlIHsKICAgIDIwMCAqIGkKICB9CiAgbWV0cmljIDwtIG51bWVyaWMoc3BsaXRfc2l6ZV90cmFpbmluZykKICBtZXRyaWNfdCA8LSBudW1lcmljKHNwbGl0X3NpemVfdHJhaW5pbmcpCiAgI21ldHJpYyA8LSBmb3JlYWNoKGk9MTpzcGxpdF9zaXplX3RyYWluaW5nKSAlZG8lIHsKICBmb3IoaSBpbiBjKDE6c3BsaXRfc2l6ZV90cmFpbmluZykpewogICAgI2xpYnJhcnkoY2FyZXQpCiAgICAjbGlicmFyeShkcGx5cikKICAgIGNvdW50IDwtIDIwMCAqIGkKICAgIGF1eF90cmFpbmluZ19zZXQgPC0gdHJhaW5pbmcuc2FtcGxlZFtjKDE6Y291bnQpLCBdI3RyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBjb3VudCksIF0KICAgIG5ld19yZkZpdCA8LSB0cmFpbihzdWJjbGFzcyB+IHNwK3dwK3ducCtzbnArZHMrZG0rZGwrc3Mrc20rc2wsCiAgICAgICAgICAgICAgICAgZGF0YSA9IGF1eF90cmFpbmluZ19zZXQsCiAgICAgICAgICAgICAgICAgbWV0cmljPSJST0MiLAogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZiIsCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gc2V0dGluZ3MpCiAgICAjVGVzdGluZyBwcmVkaWN0CiAgICBwcmVkc3JmcHJvYnMgPC0gcHJlZGljdChuZXdfcmZGaXQsdGVzdGluZyx0eXBlPSdwcm9iJykKICAgIHByZWRzcmYgPC0gaWZlbHNlKHByZWRzcmZwcm9icyRib3RuZXQgPj0wLjUsJ2JvdG5ldCcsJ25vcm1hbCcpCiAgICBjbSA8LSBjb25mdXNpb25NYXRyaXgocHJlZHNyZix0ZXN0aW5nJHN1YmNsYXNzKQogICAgbWV0cmljW2ldIDwtIGNtJGJ5Q2xhc3NbJ0YxJ10KICAgIAogICAgCiAgICAjVHJhaW5uaW5nIHByZWRpY3QKICAgIHByZWRzcmZwcm9ic190IDwtIHByZWRpY3QobmV3X3JmRml0LGF1eF90cmFpbmluZ19zZXQsdHlwZT0ncHJvYicpCiAgICBwcmVkc3JmX3QgPC0gaWZlbHNlKHByZWRzcmZwcm9ic190JGJvdG5ldCA+PSAwLjUsJ2JvdG5ldCcsJ25vcm1hbCcpCiAgICBjbV90IDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkc3JmX3QsYXV4X3RyYWluaW5nX3NldCRzdWJjbGFzcykKICAgIG1ldHJpY190W2ldIDwtIGNtX3QkYnlDbGFzc1snRjEnXQogICAgCiAgfQogIG91dHB1dCA8LSBkby5jYWxsKHJiaW5kLCBNYXAoZGF0YS5mcmFtZSwgZGF0YV9jb3VudD1jb3VudF9yYW5kb20sIG1ldHJpYz1tZXRyaWMpKQogIG91dHB1dF90IDwtIGRvLmNhbGwocmJpbmQsIE1hcChkYXRhLmZyYW1lLCBkYXRhX2NvdW50PWNvdW50X3JhbmRvbSwgbWV0cmljPW1ldHJpY190KSkKICBsaXN0X3Jlc3VsdCA8LSBsaXN0KCdvdXRwdXQnID0gb3V0cHV0LCAnb3V0cHV0X3QnID0gb3V0cHV0X3QsICd0ZXN0aW5nX3Jlc3VsdCcgPSB0ZXN0aW5nX3Jlc3VsdCkKfQoKZ2VuZXJhdGVfZGF0YV9ub2lzeSA8LSBmdW5jdGlvbihkYXRhc2V0LCBwb3JjZW50KXsKICBsaXN0X2F1eCA8LSBzYW1wbGUobnJvdyhkYXRhc2V0KSAscG9yY2VudCkKICBub2lzeV9kYXRhX3NhbXBsZSA8LSBkYXRhc2V0W2xpc3RfYXV4LF0KICBub19ub2lzeV9kYXRhX3NhbXBsZSA8LSBkYXRhc2V0Wy1saXN0X2F1eCxdCiAgCiAgbm9pc3lfZGF0YV9zYW1wbGVfYiA8LSBub2lzeV9kYXRhX3NhbXBsZSAlPiUgZmlsdGVyKGNsYXNzID09ICdCb3RuZXQnKQogIG5vaXN5X2RhdGFfc2FtcGxlX24gPC0gbm9pc3lfZGF0YV9zYW1wbGUgJT4lIGZpbHRlcihjbGFzcyA9PSAnTm9ybWFsJykKICAKICBub2lzeV9kYXRhX3NhbXBsZV9iJGNsYXNzIDwtIGFzLmNoYXJhY3Rlcihub2lzeV9kYXRhX3NhbXBsZV9iJGNsYXNzKQogIG5vaXN5X2RhdGFfc2FtcGxlX2IkY2xhc3Nbbm9pc3lfZGF0YV9zYW1wbGVfYiRjbGFzcyA9PSAnQm90bmV0J10gPC0gJ05vcm1hbCcKICBub2lzeV9kYXRhX3NhbXBsZV9iJGNsYXNzIDwtIGFzLmZhY3Rvcihub2lzeV9kYXRhX3NhbXBsZV9iJGNsYXNzKQogIAogIG5vaXN5X2RhdGFfc2FtcGxlX24kY2xhc3MgPC0gYXMuY2hhcmFjdGVyKG5vaXN5X2RhdGFfc2FtcGxlX24kY2xhc3MpCiAgbm9pc3lfZGF0YV9zYW1wbGVfbiRjbGFzc1tub2lzeV9kYXRhX3NhbXBsZV9uJGNsYXNzID09ICdOb3JtYWwnXSA8LSAnQm90bmV0JwogIG5vaXN5X2RhdGFfc2FtcGxlX24kY2xhc3MgPC0gYXMuZmFjdG9yKG5vaXN5X2RhdGFfc2FtcGxlX24kY2xhc3MpCiAgCiAgbm9pc3lfZGF0YSA8LSByYmluZChub2lzeV9kYXRhX3NhbXBsZV9iLCBub2lzeV9kYXRhX3NhbXBsZV9uKQogIHRyYWluaW5nX25vaXN5IDwtIHJiaW5kKG5vX25vaXN5X2RhdGFfc2FtcGxlLG5vaXN5X2RhdGEpCiAgdHJhaW5pbmdfbm9pc3kgPC0gdHJhaW5pbmdfbm9pc3lbc2FtcGxlKG5yb3codHJhaW5pbmdfbm9pc3kpLG5yb3codHJhaW5pbmdfbm9pc3kpKSxdCiAgcmV0dXJuKHRyYWluaW5nX25vaXN5KQp9CgpnZXRfRUxBX21lYXN1cmUgPC0gZnVuY3Rpb24oQTAsIEF4KXsKICBSTEEgPC0gYWJzKEEwIC0gQXgpIC8gQTAKICBGQTAgPC0gKDEgLSBBMCkgLyBBMAogIEVMQSA8LSBSTEEgKyBGQTAKICByZXR1cm4oRUxBKQp9CgpyYW5kb21Gb3Jlc3RfcGVyZm9ybWFjZSA8LSBmdW5jdGlvbih0cmFpbmluZ19kYXRhLCB0ZXN0aW5nX2RhdGEpewogIHJmRml0IDwtIHRyYWluKGNsYXNzIH4gc3Ard3Ard25wK3NucCtkcytkbStkbCtzcytzbStzbCwKICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5pbmdfZGF0YSwKICAgICAgICAgICAgICAgICBtZXRyaWM9IlJPQyIsCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJmIiwKICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBzZXR0aW5ncykKICBwcmVkc3JmcHJvYnMgPC0gcHJlZGljdChyZkZpdCx0ZXN0aW5nX2RhdGEsdHlwZT0ncHJvYicpCiAgcHJlZHNyZiA8LSBpZmVsc2UocHJlZHNyZnByb2JzJEJvdG5ldCA+PTAuNSwnQm90bmV0JywnTm9ybWFsJykKICBjbSA8LSBjb25mdXNpb25NYXRyaXgocHJlZHNyZix0ZXN0aW5nX2RhdGEkY2xhc3MpCiAgcmVzdWx0IDwtIGNtJGJ5Q2xhc3MKICByZXR1cm4ocmVzdWx0KQp9Cgpjb3NpbmVfc2ltaWxhcml0eSA8LSBmdW5jdGlvbih2ZWN0b3IsIG1hdHJpeCl7CiAgcmVzdWx0IDwtIGMoKQogIGZvcihpIGluIGMoMTpucm93KG1hdHJpeCkpKXsKICAgIHYxIDwtIGFzLm51bWVyaWModmVjdG9yKQogICAgdjIgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IoYXMubWF0cml4KG1hdHJpeFtpLF0pKSkKICAgIHJlc3VsdFtpXSA9IHYxICUqJSB2MiAvIHNxcnQodjEgJSolIHYxICogdjIgJSolIHYyKQogIH0KICByZXR1cm4ocmVzdWx0KQp9CgpwcmVkaWN0aW9uX2J5X3NpbWlsYXJpdHkgPC0gZnVuY3Rpb24odHJhaW5fZWxlbWVudCwgcHJlZGljdGlvbl9lbGVtZW50LG51bWJlcl9lbGVtZW50KXsKICBudW1lcmljX3RyYWluX2VsZW1lbnQgPC0gdHJhaW5fZWxlbWVudFssMToxMF0gI0dldHRpbmcgY2hhcmFjdGVyaXN0aWMgbnVtZXJpYyB2ZWN0b3IKICBzaW1pbGFyaXR5X3Jlc3VsdCA8LSBjb3NpbmVfc2ltaWxhcml0eShwcmVkaWN0aW9uX2VsZW1lbnRbMToxMF0sbnVtZXJpY190cmFpbl9lbGVtZW50KQogIHRyYWluX2VsZW1lbnQkc2ltaWxhcml0eV9yZXN1bHQgPC0gc2ltaWxhcml0eV9yZXN1bHQKICB0cmFpbl9lbGVtZW50X29yZGVyX2J5X3NpbWlsYXJpdHkgPC0gdHJhaW5fZWxlbWVudFtvcmRlcih0cmFpbl9lbGVtZW50JHNpbWlsYXJpdHlfcmVzdWx0LCBkZWNyZWFzaW5nID0gVCksXQogIHRyYWluX2VsZW1lbnRfb3JkZXJfYnlfc2ltaWxhcml0eSA8LSB0cmFpbl9lbGVtZW50X29yZGVyX2J5X3NpbWlsYXJpdHlbMTpudW1iZXJfZWxlbWVudCxdCiAgYXV4IDwtIHRyYWluX2VsZW1lbnRfb3JkZXJfYnlfc2ltaWxhcml0eSAlPiUgZ3JvdXBfYnkoY2xhc3MpICU+JSBzdW1tYXJpc2Uobj1uKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCiAgcmVzdWx0IDwtIGF1eFsxLDFdCiAgcmV0dXJuKHJlc3VsdCkKfQoKdHJhaW5pbmcKdGVzdGluZwpgYGAKCiMjIyBEYXRhIHRyYWluaW5nIHBhcnRpdGlvbnM6IGNvbGQgc3RhcnQgc3R1ZHkKIyMjIEl0ZXJhdGlvbiAjMQpgYGB7cn0Kc2V0LnNlZWQoMjAxKQpzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCnRyYWluaW5nLnNhbXBsZWRfMSA8LSB0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgc2l6ZV90cmFpbmluZyksIF0KCnJlc3VsdCA8LSBjb2xkX3N0YXJ0X2RhdGEodHJhaW5pbmcuc2FtcGxlZF8xLCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKb3V0cHV0XzEgPC0gcmVzdWx0JG91dHB1dApvdXRwdXRfdF8xIDwtIHJlc3VsdCRvdXRwdXRfdApvdXRwdXRfMQoKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfMSkKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCmZpcnN0X3RyYWluaW5nX3NhbXBsZSA8LSB0cmFpbmluZy5zYW1wbGVkXzFbMToyMDAsXQpnZ3Bsb3QoZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhzdWJjbGFzcykpCmNsYXNzX2Rpc3RyaWJ1dGlvbiA8LSBmaXJzdF90cmFpbmluZ19zYW1wbGUgJT4lIGdyb3VwX2J5KGNsYXNzKSAlPiUgc3VtbWFyaXNlKG4gPSBuKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCmdncGxvdChmaXJzdF90cmFpbmluZ19zYW1wbGUpICsgZ2VvbV9iYXIoYWVzKGNsYXNzKSkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQoKdGVzdGluZ19yZXN1bHQgPC0gcmVzdWx0JHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfdF8xKQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpvdXRwdXRfdF9hdXhfMSA8LSBvdXRwdXRfdF8xCm5hbWVzKG91dHB1dF90X2F1eF8xKSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCm91dHB1dF9yZXN1bHRfMSA8LSBjYmluZChvdXRwdXRfMSxvdXRwdXRfdF9hdXhfMSkKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfcmVzdWx0XzEpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCiMjIyBJdGVyYXRpb24gIzIKYGBge3J9CnNldC5zZWVkKDIwMikKc2l6ZV90cmFpbmluZyA8LSBucm93KHRyYWluaW5nKQp0cmFpbmluZy5zYW1wbGVkXzIgPC0gdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIHNpemVfdHJhaW5pbmcpLCBdCgpyZXN1bHRfMiA8LSBjb2xkX3N0YXJ0X2RhdGEodHJhaW5pbmcuc2FtcGxlZF8yLCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKb3V0cHV0XzIgPC0gcmVzdWx0XzIkb3V0cHV0Cm91dHB1dF90XzIgPC0gcmVzdWx0XzIkb3V0cHV0X3QKb3V0cHV0XzIKCmdnIDwtIGdncGxvdChkYXRhID0gb3V0cHV0XzIpCiAgZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogICAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgICAjc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgICBjb2xvcj1OVUxMKQpmaXJzdF90cmFpbmluZ19zYW1wbGUgPC0gdHJhaW5pbmcuc2FtcGxlZF8yWzE6MjAwLF0KZ2dwbG90KGZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpjbGFzc19kaXN0cmlidXRpb24gPC0gZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QoZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCnRlc3RpbmdfcmVzdWx0XzIgPC0gcmVzdWx0JHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfdF8yKQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpvdXRwdXRfdF9hdXhfMiA8LSBvdXRwdXRfdF8yCm5hbWVzKG91dHB1dF90X2F1eF8yKSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCm91dHB1dF9yZXN1bHRfMiA8LSBjYmluZChvdXRwdXRfMixvdXRwdXRfdF9hdXhfMikKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfcmVzdWx0XzIpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICMzCmBgYHtyfQpzZXQuc2VlZCgyMzMpCnNpemVfdHJhaW5pbmcgPC0gbnJvdyh0cmFpbmluZykKdHJhaW5pbmcuc2FtcGxlZF8zIDwtIHRyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBzaXplX3RyYWluaW5nKSwgXQoKcmVzdWx0XzMgPC0gY29sZF9zdGFydF9kYXRhKHRyYWluaW5nLnNhbXBsZWRfMywgdGVzdGluZywgc2V0dGluZ3MgPSBjdHJsX2Zhc3QpCm91dHB1dF8zIDwtIHJlc3VsdF8zJG91dHB1dApvdXRwdXRfdF8zIDwtIHJlc3VsdF8zJG91dHB1dF90Cm91dHB1dF8zCgoKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfMykKICBnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ3JlZCcpICsgCiAgICBsYWJzKHRpdGxlPSJSYW5kb20gRm9yZXN0IHRocm91Z2ggZGF0YSB0cmFpbmluZyBzaXplIiwgCiAgICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICAgIHk9IkYxIFNjb3JlIiwgCiAgICAgICAgIGNvbG9yPU5VTEwpCmZpcnN0X3RyYWluaW5nX3NhbXBsZSA8LSB0cmFpbmluZy5zYW1wbGVkXzNbMToyMDAsXQpnZ3Bsb3QoZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhzdWJjbGFzcykpCmNsYXNzX2Rpc3RyaWJ1dGlvbiA8LSBmaXJzdF90cmFpbmluZ19zYW1wbGUgJT4lIGdyb3VwX2J5KGNsYXNzKSAlPiUgc3VtbWFyaXNlKG4gPSBuKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCmdncGxvdChmaXJzdF90cmFpbmluZ19zYW1wbGUpICsgZ2VvbV9iYXIoYWVzKGNsYXNzKSkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQoKdGVzdGluZ19yZXN1bHRfMyA8LSByZXN1bHQkdGVzdGluZ19yZXN1bHQKI3Rlc3RpbmdfcmVzdWx0CgpnZyA8LSBnZ3Bsb3QoZGF0YSA9IG91dHB1dF90XzMpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKCm91dHB1dF90X2F1eF8zIDwtIG91dHB1dF90XzMKbmFtZXMob3V0cHV0X3RfYXV4XzMpIDwtIGMoJ2RhdGFfY291bnRfdCcsJ21ldHJpY190JykKb3V0cHV0X3Jlc3VsdF8zIDwtIGNiaW5kKG91dHB1dF8zLG91dHB1dF90X2F1eF8zKQpnZyA8LSBnZ3Bsb3QoZGF0YSA9IG91dHB1dF9yZXN1bHRfMykKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnRfdCwgeSA9IG1ldHJpY190KSxjb2xvciA9ICdibHVlJykgKyAKICBsYWJzKHRpdGxlPSJSYW5kb20gRm9yZXN0IHRocm91Z2ggZGF0YSB0cmFpbmluZyBzaXplIiwgCiAgICAgICAjc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgY2FwdGlvbj0iU291cmNlOiBDVFUtMTMiLCAKICAgICAgIHk9IkYxIFNjb3JlIiwgCiAgICAgICBjb2xvcj1OVUxMKQpgYGAKCiMjIyBJdGVyYXRpb24gIzQKYGBge3J9CnNldC5zZWVkKDIwNCkKc2l6ZV90cmFpbmluZyA8LSBucm93KHRyYWluaW5nKQp0cmFpbmluZy5zYW1wbGVkXzQgPC0gdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIHNpemVfdHJhaW5pbmcpLCBdCgpyZXN1bHRfNCA8LSBjb2xkX3N0YXJ0X2RhdGEodHJhaW5pbmcuc2FtcGxlZF80LCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKb3V0cHV0XzQgPC0gcmVzdWx0XzQkb3V0cHV0Cm91dHB1dF90XzQgPC0gcmVzdWx0XzQkb3V0cHV0X3QKb3V0cHV0XzQKCmdnIDwtIGdncGxvdChkYXRhID0gb3V0cHV0XzQpCiAgZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogICAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgICAjc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgICBjb2xvcj1OVUxMKQpmaXJzdF90cmFpbmluZ19zYW1wbGUgPC0gdHJhaW5pbmcuc2FtcGxlZF80WzE6MjAwLF0KZ2dwbG90KGZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpjbGFzc19kaXN0cmlidXRpb24gPC0gZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QoZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCnRlc3RpbmdfcmVzdWx0XzQgPC0gcmVzdWx0JHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfdF80KQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpvdXRwdXRfdF9hdXhfNCA8LSBvdXRwdXRfdF80Cm5hbWVzKG91dHB1dF90X2F1eF80KSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCm91dHB1dF9yZXN1bHRfNCA8LSBjYmluZChvdXRwdXRfNCxvdXRwdXRfdF9hdXhfNCkKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfcmVzdWx0XzQpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICM1CmBgYHtyfQpzZXQuc2VlZCgyMDUpCnNpemVfdHJhaW5pbmcgPC0gbnJvdyh0cmFpbmluZykKdHJhaW5pbmcuc2FtcGxlZF81IDwtIHRyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBzaXplX3RyYWluaW5nKSwgXQoKcmVzdWx0XzUgPC0gY29sZF9zdGFydF9kYXRhKHRyYWluaW5nLnNhbXBsZWRfNSwgdGVzdGluZywgc2V0dGluZ3MgPSBjdHJsX2Zhc3QpCm91dHB1dF81IDwtIHJlc3VsdF81JG91dHB1dApvdXRwdXRfdF81IDwtIHJlc3VsdF81JG91dHB1dF90Cm91dHB1dF81CgpnZyA8LSBnZ3Bsb3QoZGF0YSA9IG91dHB1dF81KQogIGdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyAKICAgIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgICAgY2FwdGlvbj0iU291cmNlOiBDVFUtMTMiLCAKICAgICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgICAgY29sb3I9TlVMTCkKZmlyc3RfdHJhaW5pbmdfc2FtcGxlIDwtIHRyYWluaW5nLnNhbXBsZWRfNVsxOjIwMCxdCmdncGxvdChmaXJzdF90cmFpbmluZ19zYW1wbGUpICsgZ2VvbV9iYXIoYWVzKHN1YmNsYXNzKSkKY2xhc3NfZGlzdHJpYnV0aW9uIDwtIGZpcnN0X3RyYWluaW5nX3NhbXBsZSAlPiUgZ3JvdXBfYnkoY2xhc3MpICU+JSBzdW1tYXJpc2UobiA9IG4oKSkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKZ2dwbG90KGZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoY2xhc3MpKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCgp0ZXN0aW5nX3Jlc3VsdF81IDwtIHJlc3VsdCR0ZXN0aW5nX3Jlc3VsdAojdGVzdGluZ19yZXN1bHQKCmdnIDwtIGdncGxvdChkYXRhID0gb3V0cHV0X3RfNSkKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdibHVlJykgKyAKICBsYWJzKHRpdGxlPSJSYW5kb20gRm9yZXN0IHRocm91Z2ggZGF0YSB0cmFpbmluZyBzaXplIiwgCiAgICAgICAjc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgY2FwdGlvbj0iU291cmNlOiBDVFUtMTMiLCAKICAgICAgIHk9IkYxIFNjb3JlIiwgCiAgICAgICBjb2xvcj1OVUxMKQoKb3V0cHV0X3RfYXV4XzUgPC0gb3V0cHV0X3RfNQpuYW1lcyhvdXRwdXRfdF9hdXhfNSkgPC0gYygnZGF0YV9jb3VudF90JywnbWV0cmljX3QnKQpvdXRwdXRfcmVzdWx0XzUgPC0gY2JpbmQob3V0cHV0XzUsb3V0cHV0X3RfYXV4XzUpCmdnIDwtIGdncGxvdChkYXRhID0gb3V0cHV0X3Jlc3VsdF81KQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ3JlZCcpICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudF90LCB5ID0gbWV0cmljX3QpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCmBgYAoKIyMjIERhdGEgdHJhaW5pbmcgcGFydGl0aW9uczogY29sZCBzdGFydCBzdHVkeSAoc2ltcGxlIFJhbmRvbSBGb3Jlc3QpCmBgYHtyfQpzZXQuc2VlZCgyMTEpCnNpemVfdHJhaW5pbmcgPC0gbnJvdyh0cmFpbmluZykKcmZfdHJhaW5pbmcuc2FtcGxlZF8xIDwtIHRyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBzaXplX3RyYWluaW5nKSwgXQpyZl9yZXN1bHRfMSA8LSBjb2xkX3N0YXJ0X2RhdGFfb25seV9yZihyZl90cmFpbmluZy5zYW1wbGVkXzEsIHRlc3RpbmcsIHNldHRpbmdzID0gY3RybF9mYXN0KQoKc2V0LnNlZWQoMjIyKQpzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCnJmX3RyYWluaW5nLnNhbXBsZWRfMiA8LSB0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgc2l6ZV90cmFpbmluZyksIF0KcmZfcmVzdWx0XzIgPC0gY29sZF9zdGFydF9kYXRhX29ubHlfcmYocmZfdHJhaW5pbmcuc2FtcGxlZF8yLCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKCnNldC5zZWVkKDIyMykKc2l6ZV90cmFpbmluZyA8LSBucm93KHRyYWluaW5nKQpyZl90cmFpbmluZy5zYW1wbGVkXzMgPC0gdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIHNpemVfdHJhaW5pbmcpLCBdCnJmX3Jlc3VsdF8zIDwtIGNvbGRfc3RhcnRfZGF0YV9vbmx5X3JmKHJmX3RyYWluaW5nLnNhbXBsZWRfMywgdGVzdGluZywgc2V0dGluZ3MgPSBjdHJsX2Zhc3QpCgpzZXQuc2VlZCgyMjQpCnNpemVfdHJhaW5pbmcgPC0gbnJvdyh0cmFpbmluZykKcmZfdHJhaW5pbmcuc2FtcGxlZF80IDwtIHRyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBzaXplX3RyYWluaW5nKSwgXQpyZl9yZXN1bHRfNCA8LSBjb2xkX3N0YXJ0X2RhdGFfb25seV9yZihyZl90cmFpbmluZy5zYW1wbGVkXzQsIHRlc3RpbmcsIHNldHRpbmdzID0gY3RybF9mYXN0KQoKc2V0LnNlZWQoMjI1KQpzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCnJmX3RyYWluaW5nLnNhbXBsZWRfNSA8LSB0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgc2l6ZV90cmFpbmluZyksIF0KcmZfcmVzdWx0XzUgPC0gY29sZF9zdGFydF9kYXRhX29ubHlfcmYocmZfdHJhaW5pbmcuc2FtcGxlZF81LCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKCnJmX2RhdGEucmVzdWx0IDwtIGRhdGEuZnJhbWUocmZfcmVzdWx0XzUkb3V0cHV0JGRhdGFfY291bnQpCnJmX2RhdGEucmVzdWx0X3QgPC0gZGF0YS5mcmFtZShyZl9yZXN1bHRfNSRvdXRwdXQkZGF0YV9jb3VudCkKZm9yKGkgaW4gYygxOjMwKSl7CiAgY3VycmVudF9zZWVkIDwtIDIyNiArIGkKICBzZXQuc2VlZChjdXJyZW50X3NlZWQpCiAgI3NpemVfdHJhaW5pbmcgPC0gbnJvdyh0cmFpbmluZykKICByZl90cmFpbmluZy5zYW1wbGVkX2N1cnJlbnQgPC0gdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIHNpemVfdHJhaW5pbmcpLCBdCiAgcmZfcmVzdWx0X2N1cnJlbnQgPC0gY29sZF9zdGFydF9kYXRhX29ubHlfcmYocmZfdHJhaW5pbmcuc2FtcGxlZF9jdXJyZW50LCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKICAKICByZl9kYXRhLnJlc3VsdCA8LSBjYmluZChyZl9kYXRhLnJlc3VsdCxyZl9yZXN1bHRfY3VycmVudCRvdXRwdXQkbWV0cmljKQogIHJmX2RhdGEucmVzdWx0X3QgPC0gY2JpbmQocmZfZGF0YS5yZXN1bHRfdCxyZl9yZXN1bHRfY3VycmVudCRvdXRwdXRfdCRtZXRyaWMpCn0KIAogeCA8LSBjKCdjb3VudF9vZl9kYXRhJykKIGZvcihpIGluIGMoMTozMCkpewogICB4W2krMV0gPC0gcGFzdGUoJ2l0ZXJhdGlvbl8nLHRvU3RyaW5nKGkpLHNlcCA9ICIiKQogfQogeAogbmFtZXMocmZfZGF0YS5yZXN1bHQpIDwtIHgKIG5hbWVzKHJmX2RhdGEucmVzdWx0X3QpIDwtIHgKIHJmX2RhdGEucmVzdWx0CiByZl9kYXRhLnJlc3VsdF90IAogCiAjd3JpdGUudGFibGUocmZfZGF0YS5yZXN1bHQsZmlsZT0icmFuZG9tX2ZvcmVzdF8zMF9pdGVyYXRpb25zX2YxX3Rlc3RpbmcudHh0IixzZXA9InwiLCByb3cubmFtZXMgPSBGKQogI3dyaXRlLnRhYmxlKHJmX2RhdGEucmVzdWx0X3QsZmlsZT0icmFuZG9tX2ZvcmVzdF8zMF9pdGVyYXRpb25zX2YxX3RyYWluaW5nLnR4dCIsc2VwPSJ8Iiwgcm93Lm5hbWVzID0gRikKYGBgCgojIyMgSXRlcmF0aW9uICMxCmBgYHtyfQpyZl9vdXRwdXRfMSA8LSByZl9yZXN1bHRfMSRvdXRwdXQKcmZfb3V0cHV0X3RfMSA8LSByZl9yZXN1bHRfMSRvdXRwdXRfdApyZl9vdXRwdXRfMQoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfMSkKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9maXJzdF90cmFpbmluZ19zYW1wbGUgPC0gcmZfdHJhaW5pbmcuc2FtcGxlZF8xWzE6MjAwLF0KZ2dwbG90KHJmX2ZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpyZl9jbGFzc19kaXN0cmlidXRpb24gPC0gcmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QocmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCiNyZl90ZXN0aW5nX3Jlc3VsdF8xIDwtIHJmX3Jlc3VsdF8xJHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfdF8xKQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9vdXRwdXRfdF9hdXhfMSA8LSByZl9vdXRwdXRfdF8xCm5hbWVzKHJmX291dHB1dF90X2F1eF8xKSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCnJmX291dHB1dF9yZXN1bHRfMSA8LSBjYmluZChyZl9vdXRwdXRfMSxyZl9vdXRwdXRfdF9hdXhfMSkKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfcmVzdWx0XzEpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICMyCmBgYHtyfQpyZl9vdXRwdXRfMiA8LSByZl9yZXN1bHRfMiRvdXRwdXQKcmZfb3V0cHV0X3RfMiA8LSByZl9yZXN1bHRfMiRvdXRwdXRfdApyZl9vdXRwdXRfMgoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfMikKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9maXJzdF90cmFpbmluZ19zYW1wbGUgPC0gcmZfdHJhaW5pbmcuc2FtcGxlZF8yWzE6MjAwLF0KZ2dwbG90KHJmX2ZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpyZl9jbGFzc19kaXN0cmlidXRpb24gPC0gcmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QocmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCiNyZl90ZXN0aW5nX3Jlc3VsdF8yIDwtIHJmX3Jlc3VsdF8yJHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfdF8yKQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9vdXRwdXRfdF9hdXhfMiA8LSByZl9vdXRwdXRfdF8yCm5hbWVzKHJmX291dHB1dF90X2F1eF8yKSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCnJmX291dHB1dF9yZXN1bHRfMiA8LSBjYmluZChyZl9vdXRwdXRfMixyZl9vdXRwdXRfdF9hdXhfMikKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfcmVzdWx0XzIpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICMzCmBgYHtyfQpyZl9vdXRwdXRfMyA8LSByZl9yZXN1bHRfMyRvdXRwdXQKcmZfb3V0cHV0X3RfMyA8LSByZl9yZXN1bHRfMyRvdXRwdXRfdApyZl9vdXRwdXRfMwoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfMykKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9maXJzdF90cmFpbmluZ19zYW1wbGUgPC0gcmZfdHJhaW5pbmcuc2FtcGxlZF8zWzE6MjAwLF0KZ2dwbG90KHJmX2ZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpyZl9jbGFzc19kaXN0cmlidXRpb24gPC0gcmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QocmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCiNyZl90ZXN0aW5nX3Jlc3VsdF8yIDwtIHJmX3Jlc3VsdF8yJHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfdF8zKQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9vdXRwdXRfdF9hdXhfMyA8LSByZl9vdXRwdXRfdF8zCm5hbWVzKHJmX291dHB1dF90X2F1eF8zKSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCnJmX291dHB1dF9yZXN1bHRfMyA8LSBjYmluZChyZl9vdXRwdXRfMyxyZl9vdXRwdXRfdF9hdXhfMykKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfcmVzdWx0XzMpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICM0CmBgYHtyfQpyZl9vdXRwdXRfNCA8LSByZl9yZXN1bHRfNCRvdXRwdXQKcmZfb3V0cHV0X3RfNCA8LSByZl9yZXN1bHRfNCRvdXRwdXRfdApyZl9vdXRwdXRfNAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfNCkKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9maXJzdF90cmFpbmluZ19zYW1wbGUgPC0gcmZfdHJhaW5pbmcuc2FtcGxlZF80WzE6MjAwLF0KZ2dwbG90KHJmX2ZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpyZl9jbGFzc19kaXN0cmlidXRpb24gPC0gcmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QocmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCiNyZl90ZXN0aW5nX3Jlc3VsdF8yIDwtIHJmX3Jlc3VsdF8yJHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfdF80KQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9vdXRwdXRfdF9hdXhfNCA8LSByZl9vdXRwdXRfdF80Cm5hbWVzKHJmX291dHB1dF90X2F1eF80KSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCnJmX291dHB1dF9yZXN1bHRfNCA8LSBjYmluZChyZl9vdXRwdXRfNCxyZl9vdXRwdXRfdF9hdXhfNCkKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfcmVzdWx0XzQpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICM1CmBgYHtyfQpyZl9vdXRwdXRfNSA8LSByZl9yZXN1bHRfNSRvdXRwdXQKcmZfb3V0cHV0X3RfNSA8LSByZl9yZXN1bHRfNSRvdXRwdXRfdApyZl9vdXRwdXRfNQoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfNSkKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9maXJzdF90cmFpbmluZ19zYW1wbGUgPC0gcmZfdHJhaW5pbmcuc2FtcGxlZF81WzE6MjAwLF0KZ2dwbG90KHJmX2ZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpyZl9jbGFzc19kaXN0cmlidXRpb24gPC0gcmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QocmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCiNyZl90ZXN0aW5nX3Jlc3VsdF8yIDwtIHJmX3Jlc3VsdF8yJHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfdF81KQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9vdXRwdXRfdF9hdXhfNSA8LSByZl9vdXRwdXRfdF81Cm5hbWVzKHJmX291dHB1dF90X2F1eF81KSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCnJmX291dHB1dF9yZXN1bHRfNSA8LSBjYmluZChyZl9vdXRwdXRfNSxyZl9vdXRwdXRfdF9hdXhfNSkKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfcmVzdWx0XzUpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgoKIyMjIFN0dWRpZXMgU2FtcGxlcwpgYGB7cn0KZmlyc3RfdHJhaW5pbmdfc2FtcGxlIDwtIHRyYWluaW5nLnNhbXBsZWRbMToyMDAsXQpmaXJzdF90cmFpbmluZ19zYW1wbGUKZ2dwbG90KGZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQoKY2xhc3NfZGlzdHJpYnV0aW9uIDwtIGZpcnN0X3RyYWluaW5nX3NhbXBsZSAlPiUgZ3JvdXBfYnkoY2xhc3MpICU+JSBzdW1tYXJpc2UobiA9IG4oKSkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKZ2dwbG90KGZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoY2xhc3MpKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCgpgYGAKCgojIyMgcGFydCAyCmBgYHtyfQpzZXQuc2VlZCgyMDYpCmxpYnJhcnkoZG9QYXJhbGxlbCkKY2wgPC0gbWFrZUNsdXN0ZXIoMikKcmVnaXN0ZXJEb1BhcmFsbGVsKGNsKQpzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCnNwbGl0X3NpemVfdHJhaW5pbmcgPSBzaXplX3RyYWluaW5nIC8gMjAwCmNvdW50X3JhbmRvbSA8LSBmb3JlYWNoKGk9MTpzcGxpdF9zaXplX3RyYWluaW5nKSAlZG9wYXIlIHsKICAyMDAgKiBpCn0KdHJhaW5pbmcuc2FtcGxlZCA8LSB0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgc2l6ZV90cmFpbmluZyksIF0KbWV0cmljIDwtIGZvcmVhY2goaT0xOnNwbGl0X3NpemVfdHJhaW5pbmcpICVkbyUgewogICNsaWJyYXJ5KGNhcmV0KQogIGNvdW50IDwtIDIwMCAqIGkKICBhdXhfdHJhaW5pbmdfc2V0IDwtIHRyYWluaW5nLnNhbXBsZWRbYygxOmNvdW50KSwgXSN0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgY291bnQpLCBdCiAgY2x1c3RlcnMgPC0ga21lYW5zKGF1eF90cmFpbmluZ19zZXRbLC1jKDExLDEyLDEzLDE0KV0sMyxuc3RhcnQgPSAyNSkKICBhdXhfdHJhaW5pbmdfc2V0X2NsdXN0ZXIgPC0gY2JpbmQoYXV4X3RyYWluaW5nX3NldCwgY2x1c3RlciA9IGNsdXN0ZXJzJGNsdXN0ZXIpCiAgcmVzdWx0X3ZlY3RvciA8LSBudW1lcmljKG5yb3codGVzdGluZykpCiAgZm9yIChqIGluIGMoMTozKSl7CiAgICBjbHVzdGVyX2RhdGEgPC0gZmlsdGVyKGF1eF90cmFpbmluZ19zZXRfY2x1c3RlciwgY2x1c3RlciA9PSBqKQogICAgbmV3X3JmRml0IDwtIHRyYWluKGNsYXNzIH4gc3Ard3Ard25wK3NucCtkcytkbStkbCtzcytzbStzbCwKICAgICAgICAgICAgICAgZGF0YSA9IGNsdXN0ZXJfZGF0YSwKICAgICAgICAgICAgICAgbWV0cmljPSJST0MiLAogICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLAogICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjdHJsX2Zhc3QpCiAgICBwcmVkc3JmcHJvYnMgPC0gcHJlZGljdChuZXdfcmZGaXQsdGVzdGluZyx0eXBlPSdwcm9iJykKICAgIGZvciAoayBpbiBjKDE6bGVuZ3RoKHJlc3VsdF92ZWN0b3IpKSl7CiAgICAgIGlmKHByZWRzcmZwcm9icyRCb3RuZXRba10gPiAwLjUpewogICAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSArIDEKICAgICAgfQogICAgICBlbHNlewogICAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSAtIDEKICAgICAgfQogICAgfQogICAgCiAgfQogIGEgPSBpZmVsc2UocmVzdWx0X3ZlY3RvciA+IDAsJ0JvdG5ldCcsJ05vcm1hbCcpCiAgY20gPC0gY29uZnVzaW9uTWF0cml4KGEsdGVzdGluZyRjbGFzcykKICBtZXRyaWMgPC0gY20kYnlDbGFzc1snRjEnXSNjbSRvdmVyYWxsWzFdCiAgbWV0cmljCiAgCn0KCm91dHB1dCA8LSBkby5jYWxsKHJiaW5kLCBNYXAoZGF0YS5mcmFtZSwgZGF0YV9jb3VudD1jb3VudF9yYW5kb20sIG1ldHJpYz1tZXRyaWMpKQpvdXRwdXQKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXQpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyAKICBsYWJzKHRpdGxlPSJSYW5kb20gRm9yZXN0IHRocm91Z2ggZGF0YSB0cmFpbmluZyBzaXplIiwgCiAgICAgICAjc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgY2FwdGlvbj0iU291cmNlOiBDVFUtMTMiLCAKICAgICAgIHk9IkFjY3VyYWN5IiwgCiAgICAgICBjb2xvcj1OVUxMKQpjbHVzdGVyX2RhdGEKYGBgCgoKIyMjIFRlc3Qgd2l0aCBvbmx5IG9uZQpgYGB7cn0Kc2V0LnNlZWQoMjI2KQpzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCnRyYWluaW5nLnNhbXBsZWQgPC0gdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIHNpemVfdHJhaW5pbmcpLCBdCgphdXhfdHJhaW5pbmdfc2V0IDwtIHRyYWluaW5nLnNhbXBsZWRbYygxOjIwMCksIF0jdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIDIwMCksIF0KY2x1c3RlcnMgPC0ga21lYW5zKGF1eF90cmFpbmluZ19zZXRbLC1jKDExLDEyLDEzLDE0KV0sMyxuc3RhcnQgPSAyNSkKYXV4X3RyYWluaW5nX3NldF9jbHVzdGVyIDwtIGNiaW5kKGF1eF90cmFpbmluZ19zZXQsIGNsdXN0ZXIgPSBjbHVzdGVycyRjbHVzdGVyKQpyZXN1bHRfdmVjdG9yIDwtIG51bWVyaWMobnJvdyh0ZXN0aW5nKSkKcmVzdWx0X3ZlY3Rvcl90cmFpbm5pbmcgPC0gbnVtZXJpYyhucm93KGF1eF90cmFpbmluZ19zZXQpKQoKZm9yIChqIGluIGMoMTozKSl7CiAgY2x1c3Rlcl9kYXRhIDwtIGF1eF90cmFpbmluZ19zZXRfY2x1c3RlciAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gaikKICBuZXdfcmZGaXQgPC0gdHJhaW4oc3ViY2xhc3MgfiBzcCt3cCt3bnArc25wK2RzK2RtK2RsK3NzK3NtK3NsLAogICAgICAgICAgICAgICBkYXRhID0gY2x1c3Rlcl9kYXRhLAogICAgICAgICAgICAgICBtZXRyaWM9IlJPQyIsCiAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZiIsCiAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGN0cmxfZmFzdCkKICBwcmVkc3JmcHJvYnMgPC0gcHJlZGljdChuZXdfcmZGaXQsdGVzdGluZyx0eXBlPSdwcm9iJykKICBmb3IgKGsgaW4gYygxOmxlbmd0aChyZXN1bHRfdmVjdG9yKSkpewogICAgaWYocHJlZHNyZnByb2JzJGJvdG5ldFtrXSA+IDAuNSl7CiAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSArIDEKICAgIH0KICAgIGVsc2V7CiAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSAtIDEKICAgIH0KICB9CiAgCiAgI1RyYWlubmluZyBwcmVkaWN0CiAgcHJlZHNyZnByb2JzX3QgPC0gcHJlZGljdChuZXdfcmZGaXQsYXV4X3RyYWluaW5nX3NldCx0eXBlPSdwcm9iJykKICBmb3IgKGsgaW4gYygxOmxlbmd0aChyZXN1bHRfdmVjdG9yX3RyYWlubmluZykpKXsKICAgIGlmKHByZWRzcmZwcm9ic190JGJvdG5ldFtrXSA+IDAuNSl7CiAgICAgIHJlc3VsdF92ZWN0b3JfdHJhaW5uaW5nW2tdIDwtIHJlc3VsdF92ZWN0b3JfdHJhaW5uaW5nW2tdICsgMQogICAgfQogICAgZWxzZXsKICAgICAgcmVzdWx0X3ZlY3Rvcl90cmFpbm5pbmdba10gPC0gcmVzdWx0X3ZlY3Rvcl90cmFpbm5pbmdba10gLSAxCiAgICB9CiAgfQp9CgphID0gaWZlbHNlKHJlc3VsdF92ZWN0b3IgPiAwLCdib3RuZXQnLCdub3JtYWwnKQpiIDwtIGlmZWxzZShyZXN1bHRfdmVjdG9yX3RyYWlubmluZyA+IDAsJ2JvdG5ldCcsJ25vcm1hbCcpCmNtIDwtIGNvbmZ1c2lvbk1hdHJpeChhLHRlc3Rpbmckc3ViY2xhc3MpCm1ldHJpYyA8LSBjbSRieUNsYXNzWydGMSddI2NtJG92ZXJhbGxbMV0KbWV0cmljCmNtX3QgPC0gY29uZnVzaW9uTWF0cml4KGIsYXV4X3RyYWluaW5nX3NldCRzdWJjbGFzcykKbWV0cmljX3QgPC0gY21fdCRieUNsYXNzWydGMSddCm1ldHJpY190CmBgYAoKIyMjIFNhbXBsZSBleGFtcGxlcwpgYGB7cn0Kc2V0LnNlZWQoNTU2KQphID0gYygxLDIsMyw0LDUsNiw3LDgsOSkKciA8LSBzYW1wbGUoOSwzKQphW3JdCnIyIDwtIHNhbXBsZSg5LDMpCmFbcjJdCmBgYAoKYGBge3J9CiN0ZXN0aW5nX3Jlc3VsdAp0ZXN0aW5nX3Jlc3VsdC5ia3AgPC0gdGVzdGluZ19yZXN1bHQKdGVzdGluZ19yZXN1bHQKbmFtZXNfYXV4IDwtIGZvcmVhY2goaT0xOihucm93KHRyYWluaW5nKS8yMDApKSAlZG8lIHsKICAgIGl0ZXJhdGlvbiA8LSAyMDAgKiBpCiAgICBwYXN0ZSgnc2l6ZV8nLHRvU3RyaW5nKGl0ZXJhdGlvbiksc2VwID0gIiIpCn0KdGVzdGluZ19yZXN1bHRfbmFtZXMgPC0gdW5saXN0KG5hbWVzX2F1eCwgdXNlLm5hbWVzPUZBTFNFKQp0ZXN0aW5nX3Jlc3VsdCA8LSB0ZXN0aW5nX3Jlc3VsdFssYygtMSldCm5hbWVzKHRlc3RpbmdfcmVzdWx0KSA8LSB0ZXN0aW5nX3Jlc3VsdF9uYW1lcwp0ZXN0aW5nX3Jlc3VsdAoKdGVzdGluZ19hdXggPC0gY2JpbmQodGVzdGluZyx0ZXN0aW5nX3Jlc3VsdCkKdGVzdGluZ19hdXguYmtwMiA8LSB0ZXN0aW5nX2F1eAojd3JpdGUudGFibGUodGVzdGluZ19hdXgsZmlsZT0idGVzdGluZ19jbHVzdGVyX3Jlc3VsdC50eHQiLHNlcD0ifCIsIHJvdy5uYW1lcyA9IEYpCnRlc3RpbmdfYXV4CnN1bXMgPC0gcm93U3Vtcyh0ZXN0aW5nX2F1eFssLWMoMToxNCldKQpzdW1zCnRlc3RpbmdfYXV4WywtYygxOjE0KV0KdGVzdGluZ19hdXggPC0gY2JpbmQodGVzdGluZ19hdXgsc3VtcykKdGVzdGluZ19hdXgKdGVzdGluZ19hdXhfcmVzdWx0IDwtIHRlc3RpbmdfYXV4ICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpLCBzdW1zID0gc3VtKHN1bXMpKSAlPiUgYXJyYW5nZShkZXNjKHN1bXMpKQp0ZXN0aW5nX2F1eF9yZXN1bHQKCmdyYXBoX3Rlc3RpbmdfcmVzdWx0IDwtIGdncGxvdCh0ZXN0aW5nX2F1eF9yZXN1bHRbLWMoMSxucm93KHRlc3RpbmdfYXV4X3Jlc3VsdCkpLF0pCmdyYXBoX3Rlc3RpbmdfcmVzdWx0ICsgZ2VvbV9wb2ludChhZXMoY2xhc3Msc3VtcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCmZlYXR1cmVfdmVjdG9yc19jbGVhbmVkCgpsaWJyYXJ5KGdyaWRFeHRyYSkKcGRmKCJkYXRhX291dHB1dC5wZGYiLCBoZWlnaHQ9MTEsIHdpZHRoPTguNSkKZ3JpZC50YWJsZShmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZFsxOjIwLF0pCmRldi5vZmYoKQoKdGVzdGluZ19yZXN1bHQuYmtwCnRlc3RpbmdfYXV4LmJrcDIKdGVzdGluZ19hdXhfcmVzdWx0CgpydXN0eV9kYXRhX3Jlc3VsdCA8LSB0ZXN0aW5nX2F1eC5ia3AyCnJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0IDwtIHJ1c3R5X2RhdGFfcmVzdWx0WywtYygxOjExLDE0KV0KcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRbLC1jKDEsMildCnJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0JHBvcyA8LSByb3dTdW1zKHJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0WywtYygxLDIpXSA+IDApCnJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0JG5lZyA8LSByb3dTdW1zKHJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0WywtYygxLDIpXSA8IDApCnJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0X2NsZWFuZWQgPC0gcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRbLGMoMSwyLDQ2LDQ3KV0KcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRfY2xlYW5lZApydXN0eV9kYXRhX3Jlc3VsdF9zaG9ydF9jbGVhbmVkX3Jlc3VsdCA8LSBydXN0eV9kYXRhX3Jlc3VsdF9zaG9ydF9jbGVhbmVkICU+JSBtdXRhdGUoZ29vZCA9IGlmZWxzZShzdWJjbGFzcyA9PSAnbm9ybWFsJyxuZWcscG9zKSkKcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRfY2xlYW5lZF9yZXN1bHQgPC0gcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRfY2xlYW5lZF9yZXN1bHQgJT4lIG11dGF0ZShiYWQgPSBpZmVsc2Uoc3ViY2xhc3MgPT0gJ25vcm1hbCcscG9zLG5lZykpCnJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0X2NsZWFuZWRfcmVzdWx0ICU+JSBncm91cF9ieShwb3J0KSAlPiUgc3VtbWFyaXNlKG49bigpLGdvb2QgPSBzdW0oZ29vZCksYmFkID0gc3VtKGJhZCkpICU+JSBhcnJhbmdlKGRlc2MobikpCgpkYXRhX2JvdG5ldF9wb3J0IDwtIHJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0X2NsZWFuZWRfcmVzdWx0ICU+JSBmaWx0ZXIoc3ViY2xhc3MgPT0gJ2JvdG5ldCcpCmRhdGFfbm9ybWFsX3BvcnQgPC0gcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRfY2xlYW5lZF9yZXN1bHQgJT4lIGZpbHRlcihzdWJjbGFzcyA9PSAnbm9ybWFsJykKZGF0YV9ib3RuZXRfcG9ydF9yZXN1bHQgPC0gIGRhdGFfYm90bmV0X3BvcnQgJT4lIGdyb3VwX2J5KHBvcnQpICU+JSBzdW1tYXJpc2Uobj1uKCksZ29vZCA9IHN1bShnb29kKSxiYWQgPSBzdW0oYmFkKSkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKZGF0YV9ub3JtYWxfcG9ydF9yZXN1bHQgPC0gZGF0YV9ub3JtYWxfcG9ydCAlPiUgZ3JvdXBfYnkocG9ydCkgJT4lIHN1bW1hcmlzZShuPW4oKSxnb29kID0gc3VtKGdvb2QpLGJhZCA9IHN1bShiYWQpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpkYXRhX2JvdG5ldF9wb3J0X3Jlc3VsdApkYXRhX25vcm1hbF9wb3J0X3Jlc3VsdAoKZ2dwbG90KGRhdGEgPSBkYXRhX2JvdG5ldF9wb3J0X3Jlc3VsdCkgKyAKICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBwb3J0LCBmaWxsID0gY2xhcml0eSkpCgojd3JpdGUudGFibGUoZGF0YV9ib3RuZXRfcG9ydF9yZXN1bHQsZmlsZT0iZGF0YV9ib3RuZXRfcG9ydC50eHQiLHNlcD0ifCIsIHJvdy5uYW1lcyA9IEYpCmxpYnJhcnkocmVzaGFwZTIpCmRhdGEgPC0gZGF0YV9ib3RuZXRfcG9ydF9yZXN1bHQKZGF0YSRwb3J0IDwtIGFzLmZhY3RvcihkYXRhJHBvcnQpCgptZWx0KGRhdGFbLGMoMSwzLDQpXSkKCmdncGxvdChtZWx0KGRhdGFbLGMoMSwzLDQpXSkpKwogIGdlb21fY29sKGFlcyh4PXBvcnQseT12YWx1ZSxmaWxsPXZhcmlhYmxlKSkrCiAgI3RoZW1lX2J3KCkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKYGBgCgojTWFraW5nIE5vaXN5IGRhdGEgKHRyYWluaW5nX25vaXN5OiBkYXRhc2V0IHRvIHRyYWluIHdpdGggMjAlIG9mIG5vaXN5KQpgYGB7cn0Kc2V0LnNlZWQoMTAxKSAKdHJhaW5pbmcuYmtwIDwtIHRyYWluaW5nCm5vaXN5X2RhdGEgPC0gdHJhaW5pbmcKCnBvcmNlbnQgPC0gbnJvdyh0cmFpbmluZykgLyA1CnRyYWluaW5nX25vaXN5IDwtIGdlbmVyYXRlX2RhdGFfbm9pc3kobm9pc3lfZGF0YSxwb3JjZW50KQpucm93KHRyYWluaW5nKQpucm93KHRyYWluaW5nX25vaXN5KQpgYGAKCmBgYHtyfQpyZkZpdCA8LSB0cmFpbihjbGFzcyB+IHNwK3dwK3ducCtzbnArZHMrZG0rZGwrc3Mrc20rc2wsCiAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluaW5nLAogICAgICAgICAgICAgICAgIG1ldHJpYz0iUk9DIiwKICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHNldHRpbmdzKQpwcmVkc3JmcHJvYnMgPC0gcHJlZGljdChyZkZpdCx0ZXN0aW5nLHR5cGU9J3Byb2InKQpwcmVkc3JmIDwtIGlmZWxzZShwcmVkc3JmcHJvYnMkQm90bmV0ID49MC41LCdCb3RuZXQnLCdOb3JtYWwnKQpjbSA8LSBjb25mdXNpb25NYXRyaXgocHJlZHNyZix0ZXN0aW5nJGNsYXNzKQpyZXN1bHQgPC0gY20kYnlDbGFzc1sxMV0KY20kb3ZlcmFsbFsxXQpgYGAKCiNSb2J1c3RuZXNzIEFuYWxpc3lzKG9uZSBzaW1wbGUgaXRlcmF0aW9uKQpgYGB7cn0Kc2V0LnNlZWQoMzIxKQpwYXJ0aXRpb25zIDwtIHNlcSgyLDkwLDIpCnJmX21lYXN1cmVzX3Jlc3VsdCA8LSBjKCkKZWxhX21lYXN1cmVzX3Jlc3VsdCA8LSBjKCkKdG90YWwgPC0gbnJvdyh0cmFpbmluZykKYmFsYW5jZWRfYWNjdXJhY3kgPC0gcmFuZG9tRm9yZXN0X3BlcmZvcm1hY2UodHJhaW5pbmcsdGVzdGluZykKCmZvcihpIGluIHBhcnRpdGlvbnMpewogIHBvcmNlbnQgPC0gKGkqdG90YWwpIC8gMTAwCiAgdHJhaW5pbmdfbm9pc3kgPC0gZ2VuZXJhdGVfZGF0YV9ub2lzeShub2lzeV9kYXRhLHBvcmNlbnQpCiAgYmFsYW5jZWRfYWNjdXJhY3lfYXV4IDwtIHJhbmRvbUZvcmVzdF9wZXJmb3JtYWNlKHRyYWluaW5nX25vaXN5LHRlc3RpbmcpCiAgcmZfbWVhc3VyZXNfcmVzdWx0W2ldIDwtIGJhbGFuY2VkX2FjY3VyYWN5X2F1eAp9Cgpmb3IoaSBpbiBwYXJ0aXRpb25zKXsKICBiYWxhbmNlZF9hY2N1cmFjeV9ub2lzeSA8LSByZl9tZWFzdXJlc19yZXN1bHRbaV0KICBlbGFfbWVhc3VyZXNfcmVzdWx0W2ldIDwtIGdldF9FTEFfbWVhc3VyZShBMCA9IGJhbGFuY2VkX2FjY3VyYWN5WydCYWxhbmNlZCBBY2N1cmFjeSddLCBBeCA9IGJhbGFuY2VkX2FjY3VyYWN5X25vaXN5KQp9CgpwbG90KGVsYV9tZWFzdXJlc19yZXN1bHQpCiBwbG90KHJmX21lYXN1cmVzX3Jlc3VsdCkKYGBgCgojUm9idXN0bmVzcyBBbmFsaXN5czogcGxvdGluZyBhY2N1cmFjeQpgYGB7cn0KaW5kZXggPC0gc2VxKDIsOTAsMikKbWVhc3VyZV9yZXN1bHQgPC0gcmZfbWVhc3VyZXNfcmVzdWx0W2luZGV4XQpybGFfbWVhc3VyZV9kYXRhIDwtIGRhdGEuZnJhbWUoaW5kZXgsbWVhc3VyZV9yZXN1bHQpCm5hbWVzKHJsYV9tZWFzdXJlX2RhdGEpIDwtIGMoJ25vaXNlX3BvcmNlbnQnLCdiYWxhbmNlZF9hY2N1cmFjeScpCgpnPC1nZ3Bsb3QocmxhX21lYXN1cmVfZGF0YSkgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IG5vaXNlX3BvcmNlbnQsIHkgPSBiYWxhbmNlZF9hY2N1cmFjeSkpICsgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4ID0gbm9pc2VfcG9yY2VudCwgeSA9IGJhbGFuY2VkX2FjY3VyYWN5KSkKZ2dwbG90bHkoZykKYGBgCgojUm9idXN0bmVzcyBBbmFsaXN5czogcGxvdGluZyBFTEEgbWVhc3VyZQpgYGB7cn0KaW5kZXggPC0gc2VxKDIsOTAsMikKbWVhc3VyZV9yZXN1bHQgPC0gZWxhX21lYXN1cmVzX3Jlc3VsdFtpbmRleF0KZWxhX21lYXN1cmVfZGF0YSA8LSBkYXRhLmZyYW1lKGluZGV4LG1lYXN1cmVfcmVzdWx0KQpuYW1lcyhlbGFfbWVhc3VyZV9kYXRhKSA8LSBjKCdub2lzZV9wb3JjZW50JywnZWxhX21lYXN1cmUnKQoKZ2dwbG90KGVsYV9tZWFzdXJlX2RhdGEpICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBub2lzZV9wb3JjZW50LCB5ID0gZWxhX21lYXN1cmUpKSArIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IG5vaXNlX3BvcmNlbnQsIHkgPSBlbGFfbWVhc3VyZSkpCmBgYAoKI1JvYnVzdG5lc3MgQW5hbGlzeXMoMzAgaXRlcmF0aW9ucykKYGBge3J9CnNldC5zZWVkKDMzMSkKcGFydGl0aW9ucyA8LSBzZXEoMiw5MCwyKQoKdG90YWwgPC0gbnJvdyh0cmFpbmluZykKcmVzdWx0X3JmX2FjY3VyYWN5IDwtIGRhdGEuZnJhbWUoJ2NvdW50X29mX2RhdGEnID0gcGFydGl0aW9ucykKcmVzdWx0X2VsYV9tZWFzdXJlIDwtIGRhdGEuZnJhbWUoJ2NvdW50X29mX2RhdGEnID0gcGFydGl0aW9ucykKd3JpdGUudGFibGUocmVzdWx0X3JmX2FjY3VyYWN5LGZpbGU9InRlc3RfcmVzdWx0X3JmX2FjY3VyYWN5LnR4dCIsc2VwPSJ8Iiwgcm93Lm5hbWVzID0gRikKd3JpdGUudGFibGUocmVzdWx0X2VsYV9tZWFzdXJlLGZpbGU9InRlc3RfcmVzdWx0X2VsYV9tZWFzdXJlLnR4dCIsc2VwPSJ8Iiwgcm93Lm5hbWVzID0gRikKZm9yIChqIGluIGMoMTozMCkpewogIHJmX21lYXN1cmVzX3Jlc3VsdCA8LSBjKCkKICBlbGFfbWVhc3VyZXNfcmVzdWx0IDwtIGMoKQogICNDYWxjdWxhbmRvIFJGIHBlcmZvcm1hbmNlIGNvbiB2YXJpYWNpb25lcyBlbiBlbCBwb3JjaWVudG8gZGUgcnVpZG8gZW4gdHJhaW5pbmcKICBmb3IoaSBpbiBwYXJ0aXRpb25zKXsKICAgIHBvcmNlbnQgPC0gKGkqdG90YWwpIC8gMTAwCiAgICB0cmFpbmluZ19ub2lzeSA8LSBnZW5lcmF0ZV9kYXRhX25vaXN5KG5vaXN5X2RhdGEscG9yY2VudCkKICAgIGJhbGFuY2VkX2FjY3VyYWN5X2F1eCA8LSByYW5kb21Gb3Jlc3RfcGVyZm9ybWFjZSh0cmFpbmluZ19ub2lzeSx0ZXN0aW5nKQogICAgcmZfbWVhc3VyZXNfcmVzdWx0W2ldIDwtIGJhbGFuY2VkX2FjY3VyYWN5X2F1eAogIH0KICAjQ2FsY3VsYW5kbyBFTEEgbWVhc3VyZQogIGZvcihpIGluIHBhcnRpdGlvbnMpewogICAgYmFsYW5jZWRfYWNjdXJhY3lfbm9pc3kgPC0gcmZfbWVhc3VyZXNfcmVzdWx0W2ldCiAgICBlbGFfbWVhc3VyZXNfcmVzdWx0W2ldIDwtIGdldF9FTEFfbWVhc3VyZShBMCA9IGJhbGFuY2VkX2FjY3VyYWN5WydCYWxhbmNlZCBBY2N1cmFjeSddLCBBeCA9IGJhbGFuY2VkX2FjY3VyYWN5X25vaXN5KQogIH0KICAKICAjUXVlZGFuZG9tZSBjb24gbGFzIHBvc2ljaW9uZXMgcGFyZXMgcXVlIHNvbiBsYXMgcXVlIHRpZW5lbiBsb3MgdmFsb3JlcwogIGVsYV9tZWFzdXJlc19yZXN1bHQgPC0gZWxhX21lYXN1cmVzX3Jlc3VsdFtwYXJ0aXRpb25zXQogIHJmX21lYXN1cmVzX3Jlc3VsdCA8LSByZl9tZWFzdXJlc19yZXN1bHRbcGFydGl0aW9uc10KICAKICBjb2x1bV9uYW1lIDwtIHBhc3RlKCdpdGVyYXRpb25fJyx0b1N0cmluZyhqKSxzZXAgPSAiIikKICByZXN1bHRfcmZfYWNjdXJhY3kgPC0gY2JpbmQocmVzdWx0X3JmX2FjY3VyYWN5LCBjb2x1bV9uYW1lID0gcmZfbWVhc3VyZXNfcmVzdWx0KQogIHJlc3VsdF9lbGFfbWVhc3VyZSA8LSBjYmluZChyZXN1bHRfZWxhX21lYXN1cmUsIGNvbHVtX25hbWUgPSBlbGFfbWVhc3VyZXNfcmVzdWx0KQogIAogIHdyaXRlLnRhYmxlKHJlc3VsdF9yZl9hY2N1cmFjeSxmaWxlPSJ0ZXN0X3Jlc3VsdF9yZl9hY2N1cmFjeS50eHQiLHNlcD0ifCIsIHJvdy5uYW1lcyA9IEYpCiAgd3JpdGUudGFibGUocmVzdWx0X2VsYV9tZWFzdXJlLGZpbGU9InRlc3RfcmVzdWx0X2VsYV9tZWFzdXJlLnR4dCIsc2VwPSJ8Iiwgcm93Lm5hbWVzID0gRikKfQoKcmVzdWx0X2VsYV9tZWFzdXJlCnJlc3VsdF9yZl9hY2N1cmFjeQoKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD04fQp4IDwtIGMoJ25vc2lzeV9wb3JjZW50JykKZm9yKGkgaW4gYygxOjMwKSl7CiB4W2krMV0gPC0gcGFzdGUoJ2l0ZXJhdGlvbl8nLHRvU3RyaW5nKGkpLHNlcCA9ICIiKQp9CngKcmVzdWx0X2VsYV9tZWFzdXJlLmJrcCA8LSByZXN1bHRfZWxhX21lYXN1cmUKcmVzdWx0X3JmX2FjY3VyYWN5LmJrcCA8LSByZXN1bHRfcmZfYWNjdXJhY3kKbmFtZXMocmVzdWx0X2VsYV9tZWFzdXJlKSA8LSB4Cm5hbWVzKHJlc3VsdF9yZl9hY2N1cmFjeSkgPC0geAoKcmVzdWx0X3JmX2FjY3VyYWN5CmdncGxvdChyZXN1bHRfcmZfYWNjdXJhY3kgJT4lIGdhdGhlcigiaXRlcmF0aW9uIiwidmFsdWUiLDI6MzEpKSsKICBnZW9tX2JveHBsb3QoYWVzKHg9YXMuZmFjdG9yKG5vc2lzeV9wb3JjZW50KSx5PXZhbHVlKSkrCiAgc3RhdF9zdW1tYXJ5KGZ1bi55PSAibWVhbiIsCiAgICAgICAgICAgICAgICAgYWVzKHg9YXMuZmFjdG9yKG5vc2lzeV9wb3JjZW50KSx5PXZhbHVlLGNvbG9yPXZhbHVlKSkKICAjc2NhbGVfY29sb3JfZ3JhZGllbnQyKGxvdyA9ICJyZWQiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiLCBtaWRwb2ludCA9IDAuNSkKYGBgCgojQ29zaW5lIFNpbWlsYXJpdHkKYGBge3J9CnRyYWluaW5nCnRlc3RpbmcKcHJlZGljdGlvbl92ZWN0b3IgPC0gdGVzdGluZ1sxLF0KcHJlZGljdGlvbl92ZWN0b3IgPC0gYXMudmVjdG9yKGFzLm1hdHJpeChwcmVkaWN0aW9uX3ZlY3RvcikpCnJlc3VsdCA8LSBwcmVkaWN0aW9uX2J5X3NpbWlsYXJpdHkodHJhaW5pbmcscHJlZGljdGlvbl92ZWN0b3IsMTAwKQpyZXN1bHQKCnJlc3VsdCA8LSBjKCkKZm9yKGkgaW4gMTpucm93KHRlc3RpbmcpKXsKICBwcmVkaWN0aW9uX3ZlY3RvciA8LSB0ZXN0aW5nW2ksXQogIHByZWRpY3Rpb25fdmVjdG9yIDwtIGFzLnZlY3Rvcihhcy5tYXRyaXgocHJlZGljdGlvbl92ZWN0b3IpKQogIHJlc3VsdFtpXSA8LSBwcmVkaWN0aW9uX2J5X3NpbWlsYXJpdHkodHJhaW5pbmcscHJlZGljdGlvbl92ZWN0b3IsMTAwKQp9CnZlY3Rvcl9yZXN1bHQgPC0gdW5saXN0KHJlc3VsdCkKY20gPC0gY29uZnVzaW9uTWF0cml4KHZlY3Rvcl9yZXN1bHQsdGVzdGluZyRjbGFzcykKY20KYGBgCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAKYGBge3J9CmNzX2RhdGEucmVzdWx0IDwtIGRhdGEuZnJhbWUocmZfcmVzdWx0XzUkb3V0cHV0JGRhdGFfY291bnQpCiNmb3IoaSBpbiBjKDE6MSkpewogIGN1cnJlbnRfc2VlZCA8LSAyMjYgIysgaQogIHNldC5zZWVkKGN1cnJlbnRfc2VlZCkKICAKICBzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCiAgY3NfdHJhaW5pbmcuc2FtcGxlZF9jdXJyZW50IDwtIHRyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBzaXplX3RyYWluaW5nKSwgXQogIHNwbGl0X3NpemVfdHJhaW5pbmcgPSBzaXplX3RyYWluaW5nIC8gMjAwCiAgbWV0cmljIDwtIG51bWVyaWMoc3BsaXRfc2l6ZV90cmFpbmluZykKICBmb3IoaiBpbiAxOnNwbGl0X3NpemVfdHJhaW5pbmcpewogICAgY291bnQgPC0gMjAwICogagogICAgYXV4X3RyYWluaW5nX3NldCA8LSBjc190cmFpbmluZy5zYW1wbGVkX2N1cnJlbnRbYygxOmNvdW50KSwgXQogICAgcmVzdWx0IDwtIGMoKQogICAgZm9yKGsgaW4gMTpucm93KHRlc3RpbmcpKXsKICAgICAgcHJlZGljdGlvbl92ZWN0b3IgPC0gdGVzdGluZ1trLF0KICAgICAgcHJlZGljdGlvbl92ZWN0b3IgPC0gYXMudmVjdG9yKGFzLm1hdHJpeChwcmVkaWN0aW9uX3ZlY3RvcikpCiAgICAgIG91dHB1dF9yZXN1bHQgPC0gcHJlZGljdGlvbl9ieV9zaW1pbGFyaXR5KGF1eF90cmFpbmluZ19zZXQscHJlZGljdGlvbl92ZWN0b3IsMTAxKQogICAgICByZXN1bHRba10gPC0gb3V0cHV0X3Jlc3VsdAogICAgfQogICAgdmVjdG9yX3Jlc3VsdCA8LSB1bmxpc3QocmVzdWx0KQogICAgY20gPC0gY29uZnVzaW9uTWF0cml4KHZlY3Rvcl9yZXN1bHQsdGVzdGluZyRjbGFzcykKICAgIG1ldHJpY1tqXSA8LSBjbSRieUNsYXNzWydGMSddCiAgICAKICB9CiAgY3NfZGF0YS5yZXN1bHQgPC0gY2JpbmQoY3NfZGF0YS5yZXN1bHQsIG1ldHJpYykKI30KY3NfZGF0YS5yZXN1bHQKYGBgCgpgYGB7cn0KCmBgYA==